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;
}

跑的还算快。

转载于:https://www.cnblogs.com/cj-chd/p/10135532.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值