POI2018
T1 Pionek
极角排序,把每个向量都当做x轴正方向,取第一、第二象限的点加起来,判断最大值即可
#include<bits/stdc++.h> #define N 402020 #define pi acos(-1) using namespace std; int n,nn,i,j,x,y;long long sx,sy,an; struct P{int x,y;double z;}p[N]; bool cmp(P a,P b){return a.z<b.z;} int main(){ scanf("%d",&nn); for(i=1;i<=nn;i++){ scanf("%d%d",&x,&y); if(x||y)p[++n]=P{x,y,atan2(y,x)}; } sort(p+1,p+n+1,cmp); for(i=j=1;i<=n;i++){ for(p[n+i]=p[i],p[n+i].z+=pi*2;j<i+n&&p[j].z<p[i].z+pi;j++){ sx+=p[j].x;sy+=p[j].y; an=max(an,sx*sx+sy*sy); } sx-=p[i].x;sy-=p[i].y; an=max(an,sx*sx+sy*sy); } printf("%lld",an); }
T2 Plan metra
给定$d_{1,i}$和$d_{n,i}$,让你还原一颗$n$个点的树,$n\leq500000$。
可以先把$1$到$n$路径上的点找出,剩下的点搭在这些路径上的点上
具体做法就是找到$a_i+b_i$最小的一些点,按照$a_i$的顺序串起来,剩下的点按$a_i-b_i$找到对应的父亲即可。
注意可能路径上没有点,那么判断一下所有的$|a_i-b_i|$是否相同,并且$|a_i-b_i|>0$即可
#include<bits/stdc++.h> #define N 505050 using namespace std; int n,i,u,t,ff,la,a[N],b[N],c[N],vs[N*8]; bool cmp(int x,int y){return a[x]-b[x]<a[y]-b[y]||a[x]-b[x]==a[y]-b[y]&&a[x]<a[y];} int main(){ scanf("%d",&n); if(n==2)return puts("TAK\n1 2 1"),0; for(i=2;i<n;i++)scanf("%d",&a[i]); for(i=2;i<n;i++)scanf("%d",&b[i]),c[i]=i;c[n]=n; for(i=2;i<n;i++)if(abs(a[i]-b[i])!=abs(a[2]-b[2]))ff=1; if(!ff&&a[2]!=b[2]){ puts("TAK"); for(i=2;i<n;i++)if(a[i]<b[i])printf("1 %d %d\n",i,a[i]);else printf("%d %d %d\n",i,n,b[i]); printf("1 %d %d\n",n,abs(a[2]-b[2])); return 0; } for(u=1e9,i=2;i<n;i++)u=min(u,a[i]+b[i]); vs[2000000-u]++;vs[u+2000000]++;b[1]=u;a[n]=u; for(i=2;i<n;i++)if(u==a[i]+b[i])if(vs[a[i]-b[i]+2000000]++)return puts("NIE"),0; for(i=2;i<n;i++)if(!vs[a[i]-b[i]+2000000])return puts("NIE"),0; puts("TAK");la=1; sort(c+2,c+n+1,cmp); for(i=2;i<=n;i++){ printf("%d %d %d\n",la,c[i],a[c[i]]-a[la]); if(a[c[i]]+b[c[i]]==u)la=c[i]; } }
T3 Powod
用最小生成树完成,每个联通块记录最大高度$mx$方案数$an$
考虑连接两个联通块$x$和$y$,高度为$z$时,如果两个联通块不联通,则对于新的联通块来说,方案数为$(an_x+z-mx_x)(an_y+z-mx_y)$,不断合并即可
#include<bits/stdc++.h> #define N 1001000 #define M 1000000007 #define fr(i,n) for(int i=1;i<=n;i++) using namespace std; int n,m,H,x,y,z,t,F[N],an[N],mx[N]; struct P{int z,x,y;}q[N]; bool cmp(P a,P b){return a.z<b.z;} int G(int x,int y){return x*m-m+y;} int cal(int z,int x){return ((z-mx[x]+M)%M+an[x])%M;} int gf(int x){return F[x]==x?x:F[x]=gf(F[x]);} int main(){ scanf("%d%d%d",&n,&m,&H); fr(i,n)fr(j,m-1)scanf("%d",&x),q[++t]=P{x,G(i,j),G(i,j+1)}; fr(i,n-1)fr(j,m)scanf("%d",&x),q[++t]=P{x,G(i,j),G(i+1,j)}; fr(i,n*m)F[i]=i,mx[i]=-1; sort(q+1,q+t+1,cmp); fr(i,t){ x=gf(q[i].x);y=gf(q[i].y);z=q[i].z; if(x!=y)F[y]=x,an[x]=1ll*cal(z,x)*cal(z,y)%M,mx[x]=z; } printf("%d",cal(H,gf(1))); }
T4 Prawnicy
即找$k$个数,使得他们$Y$的最小值减去$X$最大值最大。则将$X$排序,用堆来维护$Y$的值即可。
#include<bits/stdc++.h> #define N 1001000 using namespace std; int n,k,i,al,ar,an; struct P{int x,y,id;}p[N]; bool cmp(P a,P b){return a.x<b.x;} priority_queue<int,vector<int>,greater<int> >Q; void cg(int l,int r){if(r-l>an)an=r-l,ar=r,al=l;} int main(){ scanf("%d%d",&n,&k); for(i=1;i<=n;i++)scanf("%d%d",&p[i].x,&p[i].y),p[i].id=i; sort(p+1,p+n+1,cmp); for(i=1;i<=k;i++)Q.push(p[i].y); cg(p[k].x,Q.top()); for(i=k+1;i<=n;i++){ if(p[i].y>Q.top())Q.pop(),Q.push(p[i].y); cg(p[i].x,Q.top()); } printf("%d\n",an); for(i=1;i<=n&&k;i++)if(p[i].x<=al&&p[i].y>=ar)printf("%d ",p[i].id),k--; }
T5 Ro?norodno
一个$n$行$m$列的矩阵,对于每个$k*k$的子矩阵,求出现颜色的最大个数和总个数。$n,m\leq3000,k\leq min(n,m),a_{i,j}\leq300000$.
每个颜色都是一个矩形,求和就是矩形覆盖,最大个数要差分一下,感觉要大力线段树。。