CF1017G The Tree
阿新 • • 發佈:2020-07-13
題目傳送門
分析:
一個離譜的構造
全部是白點,我們考慮先將樹上每個節點的權值設為0
一個點進行一次操作1,我們把這個點的權值+1
設\(sum(u,v)\)表示\(u,v\)路徑上的權值和,一個節點\(u\)的深度為\(dpt_u\)
假設沒有操作2,查詢某個點\(x\)顏色時
若該點為黑色,當且僅當存在一個祖先\(y\),使得\(sum(x,y)\geq dpt_x-dpt_y+1\)
自己腦補一下過程覺得挺對的,可以類比成在某個節點加入黑球沿著路徑往下擠??(我反正是這樣理解的)
\(dpt_x-dpt_y+1\)我們可以把每個點的權值初始化為-1,求字首和時剛好可以順便減掉了
問題就變成了求到祖先路徑上的最大字尾和,樹鏈剖分加線段樹上二分可以\(O(nlog^2n)\)
現在看看操作2怎麼解決
首先把子樹內的權值全部初始化為-1,這個線段樹簡單維護
子樹根的祖先可能造成影響,假設字首最大值所在點為\(s\),我們要想辦法讓\(sum(s,x)\)變為-1,又不影響到\(x\)的祖先,並且影響到\(x\)的後代
那就直接在\(x\)處權值減去\(sum(s,x)+1\),問題就解決了,直接單點修改
總複雜度\(O(nlog^2n)\)
這就是人類智慧嗎,又被開出人籍了QwQ
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<map> #include<vector> #define maxn 200005 #define INF 0x3f3f3f3f using namespace std; inline int getint() { int num=0,flag=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1; while(c>='0'&&c<='9')num=num*10+c-48,c=getchar(); return num*flag; } int n,q; int fir[maxn],nxt[maxn],to[maxn],cnt; int sz[maxn],fa[maxn],dpt[maxn],son[maxn],tp[maxn]; int pos[maxn],cur; int sum[maxn<<2],mx[maxn<<2],lz[maxn<<2],len[maxn<<2]; inline void newnode(int u,int v) {to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt;} inline void dfs1(int u) { sz[u]=1; for(int i=fir[u];i;i=nxt[i])if(to[i]!=fa[u]) { dpt[to[i]]=dpt[u]+1,fa[to[i]]=u; dfs1(to[i]),sz[u]+=sz[to[i]]; if(sz[son[u]]<sz[to[i]])son[u]=to[i]; } } inline void dfs2(int u,int ac) { tp[u]=ac,pos[u]=++cur; if(son[u])dfs2(son[u],ac); for(int i=fir[u];i;i=nxt[i])if(to[i]!=son[u]&&to[i]!=fa[u])dfs2(to[i],to[i]); } inline void pushup(int i) { sum[i]=sum[i<<1]+sum[i<<1|1]; mx[i]=max(mx[i<<1]+sum[i<<1|1],mx[i<<1|1]); } inline void pushdown(int i) { if(lz[i]) { lz[i<<1]=lz[i<<1|1]=1; sum[i<<1]=-len[i<<1],sum[i<<1|1]=-len[i<<1|1]; mx[i<<1]=mx[i<<1|1]=-1; lz[i]=0; } } inline void build(int i,int l,int r) { len[i]=r-l+1; if(l==r){mx[i]=sum[i]=-1;return;} int mid=(l+r)>>1; build(i<<1,l,mid),build(i<<1|1,mid+1,r); pushup(i); } inline void update(int i,int l,int r,int p,int x) { if(l==r){sum[i]+=x,mx[i]+=x;return;} int mid=(l+r)>>1; pushdown(i); if(p<=mid)update(i<<1,l,mid,p,x); else update(i<<1|1,mid+1,r,p,x); pushup(i); } inline void clear(int i,int l,int r,int ql,int qr) { if(qr<l||r<ql)return; if(ql<=l&&r<=qr){sum[i]=-len[i],mx[i]=-1,lz[i]=1;return;} int mid=(l+r)>>1; pushdown(i); clear(i<<1,l,mid,ql,qr),clear(i<<1|1,mid+1,r,ql,qr); pushup(i); } inline int getsum(int i,int l,int r,int ql,int qr) { if(qr<l||r<ql)return 0; if(ql<=l&&r<=qr)return sum[i]; int mid=(l+r)>>1; pushdown(i); return getsum(i<<1,l,mid,ql,qr)+getsum(i<<1|1,mid+1,r,ql,qr); } inline int query(int i,int l,int r,int ql,int qr) { if(qr<l||r<ql)return -INF; if(ql<=l&&r<=qr)return mx[i]; int mid=(l+r)>>1; pushdown(i); return max(query(i<<1,l,mid,ql,qr)+getsum(i<<1|1,mid+1,r,ql,qr),query(i<<1|1,mid+1,r,ql,qr)); } inline int getans(int u) { int ans=-INF,ret=0; while(u) { ans=max(ans,ret+query(1,1,n,pos[tp[u]],pos[u])); ret+=getsum(1,1,n,pos[tp[u]],pos[u]),u=fa[tp[u]]; } return ans; } int main() { n=getint(),q=getint(); for(int i=2;i<=n;i++) { int u=getint(); newnode(u,i),newnode(i,u); } dfs1(1),dfs2(1,1),build(1,1,n); while(q--) { int op=getint(); if(op==1)update(1,1,n,pos[getint()],1); if(op==2) { int x=getint(); clear(1,1,n,pos[x],pos[x]+sz[x]-1); update(1,1,n,pos[x],-getans(x)-1); } if(op==3)puts(getans(getint())>=0?"black":"white"); } }