BZOJ4817 [SDOI2017]树点涂色

六眼飞鱼酱① 提交于 2019-12-01 05:05:34

Bob有一棵$n$个点的有根树,其中$1$号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。
定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。
Bob可能会进行这几种操作:

  • 1 x 把点$x$到根节点的路径上所有的点染上一种没有用过的新颜色。
  • 2 x y 求$x$到$y$的路径的权值。
  • 3 x 在以$x$为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行$m$次操作。
$n,m leqslant 10^5$。

题解

这道题我是按着LCT的标签找的,然而看到这道题的时候满脑子都是树剖……根本看不出哪里要用LCT……个人觉得这题还是蛮巧妙的。
首先,如果我们把同一种颜色的点放到LCT的一棵Splay中去,那么可以发现,1操作恰好就是LCT的access操作了。
对于一个点,他到根节点的权值,也就是LCT中这个点到根节点的实链的条数。记$f[i]$表示$i$号节点到根的权值。由这个权值的性质,我们不难得到,对于两个点$x$与$y$,$x$到$y$的路径上的权值为$$f[x]+f[y]-2f[lca(x,y)]+1$$
于是第二个操作也解决了。
第三个操作,我们不难想到把dfs序抽出来,在线段树上查询即可。
最后我们还有一个问题,也就是$f$数组该如何维护。其实不难想到,在access操作把一条虚边变为实边,我们就把这条虚边对应的子树在线段树上$-1$,若将一条实边变为虚边,就把实边对应的子树在线段树上$+1$。于是问题得到了解决。
时间复杂度$O(nlog^2n)$。
代码实现上遇到了一些麻烦,这里讲一下我写的时候遇到的几个错误:

  • 注意线段树初始化的值为这个点在原树上的深度+1。
  • 还是原树与辅助树的问题,access操作时,当我们要对线段树上修改的时候,假如我们在Splay上要把实边$u$到$v$去掉,在线段树上修改的时候,并不是直接把$dfn[v]$到$dfn[v]+size[v]-1$这一段的答案$+1$,而应该找到$v$点所在的Splay中一直往左走,走到这棵Splay中深度最浅的点,再在线段树上进行修改。

代码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
using namespace std;typedef long long ll;const int INF=1e9;const double eps=1e-9;const int maxn=1e5+10;int to[maxn<<1],nex[maxn<<1],beg[maxn],dfn[maxn],id[maxn],f[maxn][20],dep[maxn],size[maxn];int e,dfs_time,n;inline int (){    int x=0,flag=1;    char ch=getchar();    while(!isdigit(ch) && ch!='-')ch=getchar();    if(ch=='-')flag=-1,ch=getchar();    while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();    return x*flag;}struct Seg_T{    int Seg[maxn<<2],Tag[maxn<<2];    void Build(int h,int l,int r){        if(l==r){            Seg[h]=dep[id[l]];            return;        }        int mid=(l+r)>>1;        Build(h<<1,l,mid),Build(h<<1|1,mid+1,r);        Seg[h]=max(Seg[h<<1],Seg[h<<1|1]);    }    inline void Push_Down(int h){        if(Tag[h]){            Tag[h<<1]+=Tag[h],Seg[h<<1]+=Tag[h];            Tag[h<<1|1]+=Tag[h],Seg[h<<1|1]+=Tag[h];            Tag[h]=0;        }    }    void Modify(int h,int l,int r,int L,int R,int val){        if(L<=l && r<=R){            Seg[h]+=val,Tag[h]+=val;            return;        }        Push_Down(h);        int mid=(l+r)>>1;        if(L<=mid)Modify(h<<1,l,mid,L,R,val);        if(R>mid)Modify(h<<1|1,mid+1,r,L,R,val);        Seg[h]=max(Seg[h<<1],Seg[h<<1|1]);    }    int Query(int h,int l,int r,int L,int R){        if(L<=l && r<=R)return Seg[h];        Push_Down(h);        int mid=(l+r)>>1,ans=0;        if(L<=mid)ans=max(ans,Query(h<<1,l,mid,L,R));        if(R>mid)ans=max(ans,Query(h<<1|1,mid+1,r,L,R));        return ans;    }}Seg;struct LCT{#define rel(x) (ch[fa[x]][1]==x)    int ch[maxn][2],fa[maxn];    inline int isroot(int x){        return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;    }    inline void Rotate(int x){        int y=fa[x],t=fa[y],flag=rel(x);        ch[fa[ch[x][flag^1]]=y][flag]=ch[x][flag^1];        fa[x]=t;if(!isroot(y))ch[t][rel(y)]=x;        ch[fa[y]=x][flag^1]=y;    }    inline void Splay(int x){        for(;!isroot(x);Rotate(x))            if(!isroot(fa[x]))Rotate(rel(x)^rel(fa[x])?x:fa[x]);    }    inline int Getl(int x){        while(ch[x][0])x=ch[x][0];        return x;    }    inline void Access(int x){        for(int i=0;x;x=fa[i=x]){            Splay(x);            if(ch[x][1]){                int pos=Getl(ch[x][1]);                Seg.Modify(1,1,n,dfn[pos],dfn[pos]+size[pos]-1,1);            }            ch[x][1]=i;            if(i){                int pos=Getl(i);                Seg.Modify(1,1,n,dfn[pos],dfn[pos]+size[pos]-1,-1);            }        }    }}T;inline void add(int x,int y){    to[++e]=y;    nex[e]=beg[x];    beg[x]=e;}void dfs(int x,int fa){    int i;    dfn[x]=++dfs_time;id[dfs_time]=x;    f[x][0]=fa;T.fa[x]=fa;size[x]=1;    for(i=beg[x];i;i=nex[i]){        if(to[i]==fa)continue;        dep[to[i]]=dep[x]+1;        dfs(to[i],x);        size[x]+=size[to[i]];    }}inline int Get_LCA(int x,int y){    int i;    if(dep[x]<dep[y])swap(x,y);    for(i=18;i>=0;i--)        if((dep[x]-dep[y]) & (1<<i))            x=f[x][i];    if(x==y)return x;    for(i=18;i>=0;i--)        if(f[x][i]!=f[y][i])            x=f[x][i],y=f[y][i];    return f[x][0];}int main(){    int i,j,q;#ifndef ONLINE_JUDGE    freopen("BZOJ4817.in","r",stdin);    freopen("BZOJ4817.out","w",stdout);#endif    n=read();q=read();    for(i=1;i<n;i++){        int x=read(),y=read();        add(x,y);add(y,x);    }    dep[1]=1;dfs(1,0);    for(i=1;i<=18;i++)        for(j=1;j<=n;j++)            f[j][i]=f[f[j][i-1]][i-1];    Seg.Build(1,1,n);    while(q--){        int opt=read();        if(opt==1){            int x=read();            T.Access(x);        }        if(opt==2){            int x=read(),y=read(),lca=Get_LCA(x,y);            int ans=Seg.Query(1,1,n,dfn[x],dfn[x])+Seg.Query(1,1,n,dfn[y],dfn[y]);            printf("%dn",ans-2*Seg.Query(1,1,n,dfn[lca],dfn[lca])+1);        }        if(opt==3){            int x=read();            printf("%dn",Seg.Query(1,1,n,dfn[x],dfn[x]+size[x]-1));        }    }    return 0;}

原文:大专栏  BZOJ4817 [SDOI2017]树点涂色


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!