• 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
    "):printf("white
    ");
    	}
    	return 0;
    }
    
    

    跑的还算快。

  • 相关阅读:
    codeforces——模拟
    线段树水题
    编码格式分类: 前后端传递数据的编码格式contentType
    爬虫之爬取求职小网站
    auth 模块使用篇
    后端获取前端的多个数据用getlist
    字符串值的替换
    单例的5种开启方式
    forms 组件的功能和使用
    cookie和session 的初步介绍
  • 原文地址:https://www.cnblogs.com/cj-chd/p/10135532.html
Copyright © 2020-2023  润新知