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