洛谷P3703/LOJ2001/Vijos2014/BZOJ4817[SDOI2017]树点涂色(LCT+树链剖分)

因为开始每个点颜色不同,染上去的颜色都是新的,由此得出:颜色种数=颜色段数。

然后考虑操作1。它的流程是:从结点x出发,每次跳到同色的链顶端,沿途染色,然后修改链顶的父亲的指向,继续执行,最后得到一条1到x的同色链。

想到了什么?access?没错,用LCT来维护同色点集的话,每次改颜色就是裸的access。

考虑到操作3要求子树内权值最大值,我们考虑维护这个权值$w_u$,那么不难发现操作2的答案因为$w_u+w_v-2w_{lca_{u,v}}+1$(差分,$lca_{u,v}$的颜色被减了两次,加回来)

而对于操作1,每次断边会使这个子树内的$w_u+1$,连边会使子树内的$w_u-1$,可以用dfs序/树链剖分+线段树维护这一过程。

#include<cstdio>
const int N=100050;
char rB[1<<21],*rS,*rT,wB[(1<<21)+50];
int wp=-1;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short buf[15];
inline void wt(int x){
    if(wp>(1<<21))flush();
    short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    wB[++wp]=x|48;
    while(l>=0)wB[++wp]=buf[l--]|48;
    wB[++wp]='\n';
}
int G[N],to[N<<1],nxt[N<<1],cnt=0,f[N],ch[N][2],tf[N],dep[N],top[N],son[N],sz[N],id[N],dfsc=0,w[N],maxn[N<<2],addv[N<<2],n;
inline int Max(int a,int b){return a>b?a:b;}
inline void Swap(int &a,int &b){int t=a;a=b;b=t;}
inline void adde(int u,int v){
    to[++cnt]=v;nxt[cnt]=G[u];G[u]=cnt;
    to[++cnt]=u;nxt[cnt]=G[v];G[v]=cnt;
}
//树剖
void dfs1(int u,int fa){
    int i,v,mxn=0;
    dep[u]=dep[f[u]=tf[u]=fa]+1;sz[u]=1;
    for(i=G[u];i;i=nxt[i])if((v=to[i])!=fa){
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[v]>mxn){
            son[u]=v;
            mxn=sz[v];
        }
    }
}
void dfs2(int u,int topf){
    int i,v;
    top[u]=topf;w[id[u]=++dfsc]=dep[u];
    if(son[u]){
        dfs2(son[u],topf);
        for(i=G[u];i;i=nxt[i])if((v=to[i])!=tf[u]&&v!=son[u])dfs2(v,v);
    }
}
inline int lca(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])Swap(u,v);
        u=tf[top[u]];
    }
    return dep[u]<dep[v]?u:v;
}
//线段树
void build(int o,int L,int R){
    if(L==R)maxn[o]=w[L];
    else{
        int lc=o<<1,rc=lc|1,M=L+R>>1;
        build(lc,L,M);build(rc,M+1,R);
        maxn[o]=Max(maxn[lc],maxn[rc]);
    }
}
void add(int o,int L,int R,int x,int y,short k){
    if(x<=L&&y>=R){addv[o]+=k;maxn[o]+=k;}
    else{
        int lc=o<<1,rc=lc|1,M=L+R>>1;
        if(x<=M)add(lc,L,M,x,y,k);
        if(y>M)add(rc,M+1,R,x,y,k);
        maxn[o]=Max(maxn[lc],maxn[rc])+addv[o];
    }
}
int ask(int o,int L,int R,int x,int y){
    if(x<=L&&y>=R)return maxn[o];
    int lc=o<<1,rc=lc|1,M=L+R>>1,ans=0;
    if(x<=M)ans=ask(lc,L,M,x,y);
    if(y>M)ans=Max(ans,ask(rc,M+1,R,x,y));
    return ans+addv[o];
}
//LCT
inline bool nrt(int o){return ch[f[o]][0]==o||ch[f[o]][1]==o;}
inline bool dir(int o){return ch[f[o]][1]==o;}
inline void rot(int o){
    int fa=f[o];
    bool d=dir(o);
    f[o]=f[fa];
    if(nrt(fa))ch[f[fa]][dir(fa)]=o;
    if(ch[fa][d]=ch[o][d^1])f[ch[fa][d]]=fa;
    f[ch[o][d^1]=fa]=o;
}
inline void splay(int o){
    for(;nrt(o);rot(o))if(nrt(f[o]))rot((dir(o)^(dir(f[o])))?o:f[o]);
}
inline int getfa(int o){  //每次都要找到子树的根再修改
    while(ch[o][0])o=ch[o][0];
    return o;
}
inline void access(int x){
    for(int y=0,t;x;x=f[y=x]){
        splay(x);
        if(ch[x][1]){
            t=getfa(ch[x][1]);
            add(1,1,n,id[t],id[t]+sz[t]-1,1);
        }
        if(ch[x][1]=y){
            t=getfa(y);
            add(1,1,n,id[t],id[t]+sz[t]-1,-1);
        }
    }
}
int main(){
    int q,i,u,v,opt,l;
    n=rd();q=rd();
    for(i=1;i<n;++i){
        u=rd();v=rd();
        adde(u,v);
    }
    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);
    while(q--){
        opt=rd();u=rd();
        if(opt==1)access(u);
        else if(opt==2){
            l=lca(u,v=rd());
            wt(ask(1,1,n,id[u],id[u])+ask(1,1,n,id[v],id[v])-(ask(1,1,n,id[l],id[l])<<1)+1);
        }else if(opt==3)wt(ask(1,1,n,id[u],id[u]+sz[u]-1));
    }
    flush();
    return 0;
}
View Code

 

posted @ 2019-08-14 16:59  wangyuchen  阅读(155)  评论(0编辑  收藏  举报