CF1017G The Tree 树链剖分

CF1017G The Tree

LG传送门

树链剖分好题。

乍一看还以为是道沙比题,然后发现修改操作有点不一样。

但是如果你对基本操作还不太熟练,可以看看我的树链剖分总结

有三个操作:

  1. 从一个点往下染黑,是黑色节点就继续染,一直染到白色节点为止;
  2. 染白一棵子树;
  3. 查询一个点的颜色。

主要是第一个操作不好处理。由于每次只询问一个点的信息,考虑把一个点的颜色用从根节点到这个点的最大后缀和来表达:最大后缀和小于零则为白色,反之则为黑色。首先把树上每一个点的权值都定为\(-1\),对于每个1操作,把该点的权值++;对于每个2操作,把一棵子树的权值全都变成\(-1\),并在该子树的根节点减去一个权值,使得大树根节点到子树根节点的最大后缀和恰为\(-1\);对于3查询,查询一个根节点到这个点的最大后缀和,\(<0\)白色,\(\ge 0\)黑色。可以证(shou)明(wan),这样做正确地在\(O(n(log n)^2)\)的复杂度内维护了树上的信息。

如果你不会在树上维护最大后缀和,可以先考虑在区间上维护,用线段树维护一个区间和、一个区间最大后缀和就好了,合并信息的方法就很容易看出了,查询的时候也可以用同样的方法合并,放到树上来之后合并的方法还是没有变。

#include<cstdio>
#include<cctype>
#define R register
#define I inline
using namespace std;
const int S=200003,M=800003,inf=0x3f3f3f3f;
char buf[1000000],*p1,*p2;
I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}
I int rd(){
	R int f=0; R char c=gc();
	while(c<48||c>57) c=gc();
	while(c>47&&c<58) f=f*10+(c^48),c=gc();
	return f;
}
struct O{int f,g;};
struct N{int f,g,b;}a[M];
int h[S],s[S],g[S],d[S],t[S],p[S],q[S],r[S],f[S],c,e,n;
I int max(int x,int y){return x>y?x:y;}
O operator+(O x,O y){return (O){max(x.f+y.g,y.f),x.g+y.g};}
I void add(int x,int y){s[++c]=h[x],h[x]=c,g[c]=y;}
I void upd(int k,int l,int r){a[k].f=-1,a[k].g=-(r-l+1),a[k].b=1;}
I void psu(int k,int p,int q){a[k].f=max(a[q].f,a[p].f+a[q].g),a[k].g=a[p].g+a[q].g;}
I void psd(int k,int l,int r){
	if(a[k].b){
		R int p=k<<1,q=p|1,m=l+r>>1;
		upd(p,l,m),upd(q,m+1,r),a[k].b=0;
	}
}
void bld(int k,int l,int r){
	if(l==r){a[k].f=a[k].g=-1; return ;}
	R int p=k<<1,q=p|1,m=l+r>>1;
	bld(p,l,m),bld(q,m+1,r),psu(k,p,q);
}
void mdf1(int k,int l,int r,int x,int v){
	if(l==r){a[k].f+=v,a[k].g+=v; return ;}
	R int p=k<<1,q=p|1,m=l+r>>1;
	psd(k,l,r);
	if(x<=m) mdf1(p,l,m,x,v);
	else mdf1(q,m+1,r,x,v);
	psu(k,p,q);
}
void mdf2(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y){upd(k,l,r); return ;}
	R int p=k<<1,q=p|1,m=l+r>>1;
	psd(k,l,r);
	if(x<=m) mdf2(p,l,m,x,y);
	if(m<y) mdf2(q,m+1,r,x,y);
	psu(k,p,q);
}
O qry(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y) return (O){a[k].f,a[k].g};
	R int p=k<<1,q=p|1,m=l+r>>1;
	O o=(O){-inf,0};
	psd(k,l,r);
	if(x<=m) o=o+qry(p,l,m,x,y);
	if(m<y) o=o+qry(q,m+1,r,x,y);
	return o;
}
void dfs1(int x,int f){
	d[x]=d[f]+1,p[x]=f,t[x]=1;
	for(R int i=h[x],y,m=0;i;i=s[i])
		if((y=g[i])^f){
			dfs1(y,x),t[x]+=t[y];
			if(t[y]>m) q[x]=y,m=t[y];
		}
}
void dfs2(int x,int t){
	f[x]=++e,r[x]=t;
	if(q[x]) dfs2(q[x],t);
	for(R int i=h[x],y;i;i=s[i])
		if((y=g[i])^p[x]&&y^q[x])
			dfs2(y,y);
}
I int qry0(int x){
	R int o=-inf,v=0; O u;
	while(x)
		u=qry(1,1,n,f[r[x]],f[x]),o=max(o,u.f+v),v+=u.g,x=p[r[x]];
	return o;
}
int main(){
	R int Q,i,x,y;
	for(n=rd(),Q=rd(),i=2;i<=n;++i)
		x=rd(),add(x,i);
	dfs1(1,0),dfs2(1,1),bld(1,1,n);
	for(i=1;i<=Q;++i){
		x=rd(),y=rd();
		if(x==1)
			mdf1(1,1,n,f[y],1);
		if(x==2)
			mdf2(1,1,n,f[y],f[y]+t[y]-1),mdf1(1,1,n,f[y],-(qry0(y)+1));
		if(x==3)
			qry0(y)>=0?printf("black\n"):printf("white\n");
	}
	return 0;
}

跑的还算快。

posted @ 2018-12-18 10:21  newbiechd  阅读(412)  评论(0编辑  收藏  举报