Petrozavodsk Summer-2017. Warsaw U Contest
A. Connectivity
设$f[i][j]$为第$i$张图中$j$点所在连通块的编号,加边时可以通过启发式合并在$O(dn\log n)$的时间内维护出来。
对于每个点,设$h[i]$为$f[j][i]$的hash值,若两个点hash值相等,则它们在$d$张图中均连通。
#include<cstdio> typedef unsigned long long ll; const int D=200,N=5002,M=262143; int d,n,m,i,j,x,y,z,ans; int f[D][N],s[D][N],g[D][N],v[D*N*2],nxt[D*N*2],ed; ll po[D],h[N]; struct E{ll v;int w,nxt;}e[N]; int G[M+1],res[N],cur; inline void add(int z,int x,int y){v[++ed]=y;nxt[ed]=g[z][x];g[z][x]=ed;} inline void ins(ll v){ int u=v&M,i=G[u]; for(;i;i=e[i].nxt)if(e[i].v==v){ans+=e[i].w*2+1;e[i].w++;return;} ans++,e[i=res[cur--]].v=v,e[i].w=1,e[i].nxt=G[u];G[u]=i; } inline void del(ll v){ int u=v&M,i=G[u],j=i; if(e[i].v==v){ ans-=e[i].w*2-1; if(!(--e[i].w))G[u]=e[res[++cur]=i].nxt; return; } for(i=e[i].nxt;i;j=i,i=e[i].nxt)if(e[i].v==v){ ans-=e[i].w*2-1; if(!(--e[i].w))e[j].nxt=e[res[++cur]=i].nxt; return; } } void dfs(int z,int x,int y,int t){ del(h[x]); h[x]-=po[z]*f[z][x]; f[z][x]=t; ins(h[x]+=po[z]*t); for(int i=g[z][x];i;i=nxt[i])if(v[i]!=y)dfs(z,v[i],x,t); } inline void merge(int z,int x,int y){ if(f[z][x]==f[z][y])return; if(s[z][f[z][x]]>s[z][f[z][y]]){int t=x;x=y;y=t;} s[z][f[z][y]]+=s[z][f[z][x]]; add(z,x,y); add(z,y,x); dfs(z,x,y,f[z][y]); } int main(){ scanf("%d%d%d",&d,&n,&m); for(po[0]=i=1;i<d;i++)po[i]=po[i-1]*10007; for(i=1;i<=n;i++)res[++cur]=i; for(i=1;i<=n;ins(h[i++]))for(j=0;j<d;j++)f[j][i]=i,s[j][i]=1,h[i]+=po[j]*i; while(m--)scanf("%d%d%d",&x,&y,&z),merge(--z,x,y),printf("%d\n",ans); }
B. Hotter-colder
留坑。
C. Painting
将每种颜色用最左和最右位置$[l,r]$表示,对于连续的区间,需要决定顺序。
设$f[l][r]$表示考虑第$l$到第$r$个区间的最大操作代价,通过观察可以发现最优决策中第一步一定操作$l$或者$r$。
时间复杂度$O(n+m^2)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=100010,M=5005; int n,m,i,j,cnt,x,y,l[M],r[M],a[N],pos[N];ll s[M],f[M][M],ans; ll fast(){ int i,j; for(i=1;i<=cnt;i++)s[i]+=s[i-1]; for(i=cnt;i;i--)for(j=i;j<=cnt;j++) f[i][j]=max(f[i][j-1],f[i+1][j])+s[j]-s[i-1]; return f[1][cnt]; } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++)scanf("%d",&a[i]); for(i=1;i<=n;i++)r[a[i]]=i; for(i=n;i;i--)l[a[i]]=i; for(i=1;i<=m;i++)pos[l[i]]=i; for(i=1;i<=n;i++)if(pos[i]){ cnt=0; x=i; while(pos[x]){ y=pos[x]; pos[x]=0; s[++cnt]=r[y]-l[y]+1; x=r[y]+1; } ans+=fast(); } printf("%lld",ans); }
D. Ones
倍增构造即可。
#include<cstdio> #include<iostream> #include<string> using namespace std; int T,n; string gao(int n){ if(n==1)return "1"; if(n==2)return "1+1"; if(n==3)return "1+1+1"; int t=n/2; string o=gao(t); string oo="("+o+")*(1+1)"; if(t+t==n)return oo; return "("+oo+")+1"; } int main(){ cin>>T; while(T--){ cin>>n; cout<<gao(n)<<endl; } }
E. Seats
留坑。
F. Ants
树链剖分,每条重链带上顶端到父亲的那条轻边,那么每只蚂蚁可以拆成$O(\log n)$份,每份对应一条重链。
对于每条重链单独考虑,把每条蚂蚁在这条重链上的行动轨迹看作二维平面上的线段,横坐标为到重链顶端的距离,纵坐标为时间,那么一共有两类相互垂直的线段。
将每条线段都看作矩形,扫描线线段树维护区间加、区间最大值查询即可。
时间复杂度$O(m\log n\log m)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=100005,M=N*36,K=524297; int n,m,cp,ce,len,i,x,y,z,g[N],v[N<<1],w[N<<1],nxt[N<<1],ed,ans[N]; int f[N],d[N],sz[N],son[N],top[N];ll s[N]; int G[N],NXT[M],ED,val[K],tag[K],cur;ll h[N<<1]; struct P{ ll x,y; P(){} P(ll _x,ll _y){x=_x,y=_y;} void rot(){ ll t=y; y=x-y; x+=t; } void fix(){swap(x,y);} }; struct E{ P s,t;int o; void rot(){ s.rot(); t.rot(); } void fix(){ s.fix(); t.fix(); if(s.x>t.x||s.x==t.x&&s.y>t.y)swap(s,t); } }e[M],pool[N]; struct EV{ ll x;int o,t; EV(){} EV(ll _x,int _o,int _t){x=_x,o=_o,t=_t;} }ev[N*3]; inline bool cmp(const EV&a,const EV&b){return a.x==b.x?a.t>b.t:a.x<b.x;} inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} void dfs(int x,int y){ sz[x]=1; for(int i=g[x];i;i=nxt[i]){ int u=v[i]; if(u==y)continue; f[u]=x; d[u]=d[x]+1; s[u]=s[x]+w[i]; dfs(u,x); sz[x]+=sz[u]; if(sz[u]>sz[son[x]])son[x]=u; } } void dfs2(int x,int y){ top[x]=y; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]&&v[i]!=son[x])dfs2(v[i],v[i]); } inline int lca(int x,int y){ while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]])swap(x,y); x=f[top[x]]; } return d[x]<d[y]?x:y; } inline void ext(int x,ll A,ll B,ll C,ll D,int o){ e[++ED].s=P(A,B); e[ED].t=P(C,D); e[ED].o=o; NXT[ED]=G[x]; G[x]=ED; } inline void split(int o){ int x,y,z,t;ll A,B,C; scanf("%d%d%d",&x,&y,&t); z=lca(x,y); A=t; B=A+s[x]+s[y]-s[z]*2; while(top[x]!=top[z]){ t=f[top[x]]; C=s[x]-s[t]; ext(top[x],s[x],A,s[t],A+C,o); x=t; A+=C; } if(x!=z){ t=z; C=s[x]-s[t]; ext(top[x],s[x],A,s[t],A+C,o); } while(top[y]!=top[z]){ t=f[top[y]]; C=s[y]-s[t]; ext(top[y],s[y],B,s[t],B-C,o); y=t; B-=C; } if(y!=z){ t=z; C=s[y]-s[t]; ext(top[y],s[y],B,s[t],B-C,o); } if(x==z&&y==z)ext(top[x],s[x],A,s[x],A,o); } void build(int x,int a,int b){ val[x]=tag[x]=0; if(a==b)return; int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); } inline void tag1(int x,int p){val[x]+=p;tag[x]+=p;} inline void pb(int x){if(tag[x])tag1(x<<1,tag[x]),tag1(x<<1|1,tag[x]),tag[x]=0;} void change(int x,int a,int b,int c,int d,int p){ if(c<=a&&b<=d){tag1(x,p);return;} pb(x); int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c,d,p); if(d>mid)change(x<<1|1,mid+1,b,c,d,p); val[x]=max(val[x<<1],val[x<<1|1]); } inline void up(int&a,int b){a<b?(a=b):0;} void ask(int x,int a,int b,int c,int d){ if(c<=a&&b<=d){up(cur,val[x]);return;} pb(x); int mid=(a+b)>>1; if(c<=mid)ask(x<<1,a,mid,c,d); if(d>mid)ask(x<<1|1,mid+1,b,c,d); } inline void gao(int o){ int i,j,x,y,t,_; cp=0; for(i=G[o];i;i=NXT[i])pool[++cp]=e[i]; for(i=1;i<=cp;i++)pool[i].rot(); for(i=0;i<2;i++){ len=ce=0; for(j=1;j<=cp;j++){ pool[j].fix(); ev[++ce]=EV(pool[j].s.x,j,1); ev[++ce]=EV(pool[j].t.x,j,-1); if(pool[j].s.x==pool[j].t.x)ev[++ce]=EV(pool[j].s.x,j,0); h[++len]=pool[j].s.y; h[++len]=pool[j].t.y; } sort(h+1,h+len+1); for(_=0,j=1;j<=len;j++)if(j==1||h[j]>h[j-1])h[++_]=h[j]; len=_; sort(ev+1,ev+ce+1,cmp); build(1,1,len); for(j=1;j<=ce;j++){ o=ev[j].o; t=ev[j].t; x=lower_bound(h+1,h+len+1,pool[o].s.y)-h; y=lower_bound(h+1,h+len+1,pool[o].t.y)-h; if(t)change(1,1,len,x,y,t); else{ cur=0; ask(1,1,len,x,y); up(ans[pool[o].o],cur); } } } } int main(){ scanf("%d%d",&n,&m); for(i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z); dfs(1,0); dfs2(1,1); for(i=1;i<=m;i++)split(i); for(i=1;i<=n;i++)if(G[i])gao(i); for(i=1;i<=m;i++)printf("%d\n",ans[i]-1); }
G. Permutation
设$f[i][j][k]$表示考虑前$i$个数,最后一个与$i$不在一起的数为$j$,$i$位于第$k$个数列是否可行。
转移可以用set实现,时间复杂度$O(n\log n)$。
#include<cstdio> #include<set> using namespace std; const int N=100010; int Case,n,i,o,x,y,a[N],b[N],c[N],g[N][2],ca,cb,qa[N],qb[N]; set<int>f0,f1; void solve(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&a[i]),b[a[i]]=i; f0.clear(); f1.clear(); f0.insert(N); f1.insert(0); for(i=1;i<n;i++){ bool A=0,B=0; set<int>::iterator it=f1.lower_bound(a[i+1]); if(it!=f1.begin())it--; if(it!=f1.end())if((*it)<a[i+1])A=1; it=f0.lower_bound(a[i+1]); if(it!=f0.end())if((*it)>a[i+1])B=1; if(a[i+1]>a[i])f1.clear();else f0.clear(); if(A)f0.insert(a[i]); if(B)f1.insert(a[i]); g[i+1][0]=g[i+1][1]=0; if(f0.size())g[i+1][0]=*f0.rbegin(); if(f1.size())g[i+1][1]=*f1.begin(); //printf("%d:\n",i+1); //for(it=f0.begin();it!=f0.end();it++)printf("%d ",*it);puts(""); //for(it=f1.begin();it!=f1.end();it++)printf("%d ",*it);puts(""); } //for(i=2;i<=n;i++)printf("%d %d %d\n",i,g[i][0],g[i][1]); if(!f0.size()&&!f1.size()){ puts("NO"); return; } x=n; if(f0.size())o=0; if(f1.size())o=1; while(x){ y=g[x][o]; if(y>=1&&y<=n)y=b[y];else y=0; for(i=y+1;i<=x;i++)c[i]=o^1; x=y,o^=1; } ca=cb=0; for(i=1;i<=n;i++)if(c[i])qa[++ca]=a[i];else qb[++cb]=a[i]; puts("YES"); printf("%d",ca); for(i=1;i<=ca;i++)printf(" %d",qa[i]);puts(""); printf("%d",cb); for(i=1;i<=cb;i++)printf(" %d",qb[i]);puts(""); } int main(){ scanf("%d",&Case); while(Case--){ solve(); } }
H. Primes
莫比乌斯反演,分段计算。
时间复杂度$O(n+q\sqrt{n})$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=1000010; int i,j,v[N],mu[N],f[N],tot,p[N],g[N]; ll sg[N],sf[N],ans; ll w[N]; int T,n,m; inline ll cal(int n,int m){ if(!n||!m)return 0; if(n==m)return w[n]; ll ret=0; for(int i=1,j;i<=n&&i<=m;i=j+1){ j=min(n/(n/i),m/(m/i)); ret+=(sg[j]-sg[i-1])*(n/i)*(m/i); } return ret; } int main(){ for(mu[1]=1,i=2;i<N;i++){ if(!v[i]){ p[tot++]=i; mu[i]=-1; for(j=i;j<N;j+=i)f[j]++; } for(j=0;j<tot&&i*p[j]<N;j++){ v[i*p[j]]=1; if(i%p[j])mu[i*p[j]]=-mu[i];else break; } } for(i=1;i<N;i++)for(j=i;j<N;j+=i)g[j]+=f[i]*mu[j/i]; for(i=1;i<N;i++)sf[i]=sf[i-1]+f[i],sg[i]=sg[i-1]+g[i]; for(i=1;i<N;i++)for(j=i;j<N;j+=i)w[j]+=1LL*(j/i)*g[i]; for(i=1;i<N;i++)w[i]=w[i-1]+w[i]*2-f[i]; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); ans=cal(m,m)-cal(n-1,m)*2+cal(n-1,n-1); ans-=sf[m]-sf[n-1]; ans/=2; printf("%lld\n",ans); fflush(stdout); } }
I. Vertex covers
留坑。
J. Scheduling
将时间离散化后网络流。
#include<cstdio> #include<algorithm> using namespace std; const int N=510,inf=~0U>>2; struct E{int t,f;E*nxt,*pair;}*g[N],*d[N],pool[N*N*2],*cur=pool; int n,m,i,j,cnt,a[110][3],b[N],ans,S,T,h[N],gap[N]; inline void add(int s,int t,int f){ E*p=cur++;p->t=t;p->f=f;p->nxt=g[s];g[s]=p; p=cur++;p->t=s;p->f=0;p->nxt=g[t];g[t]=p; g[s]->pair=g[t];g[t]->pair=g[s]; } int sap(int v,int flow){ if(v==T)return flow; int rec=0; for(E*p=d[v];p;p=p->nxt)if(h[v]==h[p->t]+1&&p->f){ int ret=sap(p->t,min(flow-rec,p->f)); p->f-=ret;p->pair->f+=ret;d[v]=p; if((rec+=ret)==flow)return flow; } if(!(--gap[h[v]]))h[S]=T; gap[++h[v]]++;d[v]=g[v]; return rec; } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%d%d",&a[i][1],&a[i][2]); scanf("%d",&a[i][0]); a[i][2]--; b[++cnt]=a[i][1]; b[++cnt]=a[i][1]+1; b[++cnt]=a[i][2]; b[++cnt]=a[i][2]+1; } sort(b+1,b+cnt+1); for(j=0,i=1;i<=cnt;i++)if(i==1||b[i]!=b[i-1])b[++j]=b[i]; cnt=j; b[cnt+1]=b[cnt]+1; S=n+cnt+1; T=S+1; for(i=1;i<=cnt;i++)add(n+i,T,(b[i+1]-b[i])*m); for(ans=0,i=1;i<=n;i++){ add(S,i,a[i][0]);ans+=a[i][0]; for(j=1;j<=cnt;j++)if(a[i][1]<=b[j]&&b[j+1]-1<=a[i][2])add(i,n+j,b[j+1]-b[j]); } for(gap[0]=T,i=1;i<=T;i++)d[i]=g[i]; while(h[S]<T)ans-=sap(S,inf); puts(ans?"NO":"YES"); }
K. Shuffle
不难发现操作两次等于没操作,故将操作次数模$2$即可。
#include<cstdio> #include<algorithm> using namespace std; int n,T,i,a[2222222]; //[l,r) void solve(int l,int r){ if(r-l<=1)return; if(r-l==2){ swap(a[l],a[l+1]); return; } int mid=(l+r)>>1; solve(l,mid); solve(mid,r); for(int i=l,j=mid;i<mid;i++,j++)swap(a[i],a[j]); } int main(){ scanf("%d%d",&n,&T); T%=2; for(i=0;i<1<<n;i++)scanf("%d",&a[i]); while(T--){ solve(0,1<<n); } for(i=0;i<1<<n;i++)printf("%d ",a[i]); }