Codechef August Challenge 2018 : Chef at the River
(要是没有tjm(Sakits)的帮忙,我还真不知道啥时候能做出来
结论是第一次带走尽可能少的动物,使未带走的动物不冲突,带走的这个数量就是最优解。
首先这个数量肯定是下界,更少的话连第一次都带不走。
然后考虑带过去之后把某一只留在对岸,剩下的全部随身携带,这时有了一个空位,就可以慢慢把与留下的动物无关联的都挪到对岸去。再把随身携带的动物中与未到达对岸的任何一只都无关联的动物留着,其余的(包括之前单出来的)随身携带,这时有了一个空位可以再次慢慢挪。
唯一的特殊情况是某一个点连接了剩余所有点,且总点数大于3,特判即可。
接着是如何计算第一次最少带多少只的问题。
取父亲必定不比取儿子差,我们可以维护这个贪心,这样就只有一种解。
考虑某次多了一个点,肯定是从这个点往上某条链的状态取反,这条链延伸的终点必定是一个已取的点有其他未取的儿子。
树剖之后维护每个点未取的轻儿子的个数,每次查询需要在线段树上二分一下。虽然看起来是两个log,但是明显跑不满,可以A(水)掉。
#include<cstdio> #include<algorithm> #define MN 1100000 #define lp p<<1 #define rp p<<1|1 using namespace std; int n,T,f[MN],_s[MN],l[MN],num,df[MN],lo[MN],s[MN],nm,son[MN],to[MN],ANS,t[MN<<2],Z[MN<<2],c[MN],w[MN],st[MN]; bool b[MN<<2]; inline int max(int x,int y){return x>y?x:y;} inline int min(int x,int y){return x<y?x:y;} struct na{int y,ne;}B[MN<<1]; inline void add(int x,int y){B[++num].y=y;B[num].ne=l[x];l[x]=num;} void dfs(int x){ s[x]=1;son[x]=0; for (int i=l[x];i;i=B[i].ne) if (dfs(B[i].y),s[x]+=s[B[i].y],s[B[i].y]>s[son[x]]) son[x]=B[i].y; } void DFS(int x,int q){ df[x]=++nm;to[x]=q; if (son[x]) DFS(son[x],q); for (int i=l[x];i;i=B[i].ne) if (B[i].y!=son[x]) DFS(B[i].y,B[i].y); lo[x]=nm; } inline void rev(int p){ b[p]^=1; } inline void pd(int p,int l,int r){ if (b[p]) if (b[p]=0,l!=r) rev(lp),rev(rp); else c[l]^=1; } void fz(int p,int l,int r,int L,int R){ if (l==L&&r==R) {rev(p);return;} pd(p,l,r); int mid=l+r>>1; if (R<=mid) fz(lp,l,mid,L,R);else if (L>mid) fz(rp,mid+1,r,L,R);else fz(lp,l,mid,L,mid),fz(rp,mid+1,r,mid+1,R); } int ask(int p,int l,int r,int L,int R){ //printf("%d %d %d %d %d %d %d %d\n",p,l,r,L,R,t[p],t[lp],t[rp]); if (!t[p]) return 0; if (l==L&&r==R) return Z[p]; pd(p,l,r); int mid=l+r>>1,tmp; if (R<=mid) return ask(lp,l,mid,L,R);else if (L>mid) return ask(rp,mid+1,r,L,R);else if (tmp=ask(rp,mid+1,r,mid+1,R)) return tmp;else return ask(lp,l,mid,L,mid); } bool col(int p,int l,int r,int k){ pd(p,l,r); if (l==r) return c[k]; int mid=l+r>>1; if (k<=mid) return col(lp,l,mid,k);else return col(rp,mid+1,r,k); } void ADD(int p,int l,int r,int k,int v){ t[p]+=v; if (l==r){ Z[p]=t[p]?l:0; return; } int mid=l+r>>1; if (k<=mid) ADD(lp,l,mid,k,v);else ADD(rp,mid+1,r,k,v); if (t[p]) if (t[rp]) Z[p]=Z[rp];else Z[p]=Z[lp];else Z[p]=0; } inline void work(int x){ int tmp,top=0,la=0; while (x){ if (son[x]&&col(1,1,n,df[x]+1)) break; if (tmp=ask(1,1,n,df[to[x]],df[x])){ if (tmp!=df[x]) fz(1,1,n,tmp+1,df[x]),la=tmp+1; break; } //printf("===%d %d\n",x,tmp); fz(1,1,n,df[to[x]],df[x]); la=df[to[x]]; if (f[to[x]]){ if (col(1,1,n,df[to[x]])){ st[++top]=f[to[x]]; }else if (--w[f[to[x]]]==0) ADD(1,1,n,df[f[to[x]]],-1); } x=f[to[x]]; } while (top){ if (w[st[top]]++==0) ADD(1,1,n,df[st[top]],1); top--; } ANS+=!col(1,1,n,la); //printf("la:%d %d\n",la,col(1,1,n,la)); //for (int j=1;j<=n;j++) printf("col %d %d:%d\n",j,df[j],col(1,1,n,df[j])); } int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); for (int i=1;i<=n;i++) _s[i]=l[i]=s[i]=son[i]=c[i]=w[i]=to[i]=0;num=nm=ANS=0; for (int i=1;i<=(n<<2);i++) t[i]=b[i]=Z[i]=0; for (int i=2;i<=n;i++){ scanf("%d",&f[i]); add(f[i],i); } dfs(1); DFS(1,1); work(1); for (int i=2;i<=n;i++){ _s[f[i]]++; work(i); printf("%d ",ANS+1+((_s[1]==i-1&&i>3||_s[1]==1&&_s[2]==i-2&&i>3))); //printf("%d\n",ANS); } puts(""); } }