• BZOJ4817 [SDOI2017]树点涂色


     

    4817: [SDOI2017]树点涂色

    Description

    Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
    径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
    1 x:
    把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
    2 x y:
    求x到y的路径的权值。
    3 x
    在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
    Bob一共会进行m次操作

    Input

    第一行两个数n,m。
    接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
    接下来m行,表示操作,格式见题目描述
    1<=n,m<=100000

    Output

    每当出现2,3操作,输出一行。
    如果是2操作,输出一个数表示路径的权值
    如果是3操作,输出一个数表示权值的最大值
     
     
    题目简述
    定义树根为1
    定义路径权值:连续颜色段的数量
    维护一棵树,支持如下三种操作:
    1、将x点到根路径上每一个点都修改成新的颜色
    2、查询某条路径权值
    3、查询某颗子树内点到1路径最大权值。
     
     
    题解
    一道非常典型的数据结构之间的相互应用的题,利用$LCT$维护路径,利用线段树维护点权最值。
     
     
    我们还是先从树剖想起,于是就发现树剖套线段树能完美的解决前两种操作,但对第三种操作却束手无策。树链剖分的限制是,在任何时刻求单个点到跟的路径权值都需要$O(log(n))$的复杂度,并且无法区间求最值,所以树剖这条路看起来并不是这么可行(但据我们机房某julao说可以直接用树剖套两个线段树搞定)。
     
    为了方便,我们设一个点的点权为其到根节点的路径权值。
     
    我们换个思路,不难发现,对于第二种操作,假设给定了路径两个端点为$(u,v)$,设$lca(u,v)=k$,设$D(x)$为$x$的点权,则$(u,v)$路径权值为和$D(u)+D(y)-2D(k)+1$,就把我第二种操作转化为求某个点的点权了。所欲对于后两个操作,我们现在只需要维护区间点权最值即可。
     
    再来关注修改,我们发现,修改与$LCT$中的$access$操作十分相似,我们考虑修改对每个点的答案的影响,如下图:

    红色部分是我们要修改的,对于每一个和红色有重叠的颜色段(即$access$操作中每一条原来的重链),重叠部分的染色对于原颜色段的除去与要修改的重叠以外部分(若不存在则不考虑)所在的整颗的子树的答案有+1的贡献(即图中各个颜色段与红色不重叠的部分);对于除去顶部的颜色段以外的所有颜色段的顶端的子树有-1的贡献。

    对应的$LCT$中就恰好是,$access$时:

    对于断开某条重链,就把下方的断点所在子树答案+1;

    对于连接一条链,就把下方连接点所在子树答案-1;

    用线段树维护即可,复杂度约为$O(ncdot log^2(n))$。

    注意初始时每个点颜色不同,点权为其深度。

    AC代码如下:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define mid (l+r>>1)
    #define M 100010
    #define ls c[x][0]
    #define rs c[x][1]
    using namespace std;
    int read(){
    	int nm=0,fh=1;char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    void write(int x){
    	if(x>=10) write(x/10);
    	putchar(x%10+'0');
    }
    int n,m,fs[M],tmp,to[M<<1],nt[M<<1],st[M<<2][20];
    int dfn[M],tk[M],cnt,dep[M],sz[M],fst[M],tot,fa[M];
    int p[M<<2],tg[M<<2],lg[M<<2],sq[30];
    int u,v,T,c[M][2],tpe;
    
    void link(int x,int y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;}
    void dfs(int x){
    	dfn[x]=++cnt,st[++tot][0]=dfn[x];
    	sz[x]=1,fst[x]=tot,tk[cnt]=x;
    	for(int i=fs[x];i!=-1;i=nt[i]){
    		if(dep[to[i]]>0) continue;
    		dep[to[i]]=dep[x]+1,dfs(to[i]),fa[to[i]]=x;
    		st[++tot][0]=dfn[x],sz[x]+=sz[to[i]];
    	}
    }
    void init(){
    	lg[0]=-1,sq[0]=1;
    	for(int i=1;i<=tot;i++) lg[i]=lg[i>>1]+1;
    	for(int i=1;i<=20;i++) sq[i]=(sq[i-1]<<1);
    	for(int j=1;j<19;j++){
    		for(int i=1;i+sq[j]-1<=tot;i++){
    			st[i][j]=min(st[i][j-1],st[i+sq[j-1]][j-1]);
    		}
    	}
    }
    int lca(int x,int y){
    	int t1=fst[x],t2=fst[y],len;
    	if(t1>t2) swap(t1,t2);
    	len=t2-t1+1;
    	return tk[min(st[t1][lg[len]],st[t2-sq[lg[len]]+1][lg[len]])];
    }
    
    void pushdown(int x){
    	tg[x<<1]+=tg[x],p[x<<1]+=tg[x];
    	tg[x<<1|1]+=tg[x],p[x<<1|1]+=tg[x];
    	tg[x]=0; return;
    }
    void pushup(int x){p[x]=max(p[x<<1],p[x<<1|1]);}
    void build(int x,int l,int r){
    	if(l==r){p[x]=dep[tk[l]];return;}
    	build(x<<1,l,mid),build(x<<1|1,mid+1,r),pushup(x);
    }
    void add(int x,int l,int r,int L,int R,int dt){
    	if(r<L||R<l) return;
    	if(L<=l&&r<=R){tg[x]+=dt,p[x]+=dt;return;}
    	pushdown(x),add(x<<1,l,mid,L,R,dt);
    	add(x<<1|1,mid+1,r,L,R,dt),pushup(x);
    }
    int query(int x,int l,int r,int L,int R){
    	if(r<L||R<l) return 0;
    	if(L<=l&&r<=R) return p[x];
    	pushdown(x); int now=query(x<<1,l,mid,L,R);
    	now=max(query(x<<1|1,mid+1,r,L,R),now);
    	pushup(x); return now;
    }
    
    bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
    void rotate(int x){
    	int tp=fa[x],dtp=fa[fa[x]],ds,ms;
    	if(!isroot(tp)){
    		if(c[dtp][0]==tp) c[dtp][0]=x;
    		else c[dtp][1]=x;
        }
        if(c[tp][0]==x) ms=0,ds=1;
        else ms=1,ds=0;
        fa[x]=dtp,fa[tp]=x,fa[c[x][ds]]=tp;
        c[tp][ms]=c[x][ds],c[x][ds]=tp;
    }
    void splay(int x){
    	while(!isroot(x)){
    		int tp=fa[x];
    		if(isroot(tp)) return rotate(x);
    		if(c[c[fa[tp]][0]][0]==x) rotate(tp);
    		else if(c[c[fa[tp]][1]][1]==x) rotate(tp);
    		else rotate(x);
    	}
    }
    void access(int x){
    	int now;
    	for(int last=0;x>0;rs=last,last=x,x=fa[x]){
    		splay(x);
    		if(rs>0){
    			for(now=rs;c[now][0]>0;now=c[now][0]);
    			add(1,1,n,dfn[now],dfn[now]+sz[now]-1,1);
    		}
    		if(last>0){
    			for(now=last;c[now][0]>0;now=c[now][0]);
    			add(1,1,n,dfn[now],dfn[now]+sz[now]-1,-1);
    		}
    	}
    }
    int main(){
    	n=read(),T=read(),memset(fs,-1,sizeof(fs));
    	for(int i=1;i<n;i++) u=read(),v=read(),link(u,v),link(v,u);
    	dep[1]=1,dfs(1),init(),build(1,1,n);
    	while(T--){
    		tpe=read(),u=read();
    		if(tpe==3) write(query(1,1,n,dfn[u],dfn[u]+sz[u]-1)),putchar('
    ');
    		else if(tpe==1) access(u);
    		else{
    			v=read(),m=lca(u,v);
    			u=query(1,1,n,dfn[u],dfn[u]);
    			v=query(1,1,n,dfn[v],dfn[v]);
    			m=query(1,1,n,dfn[m],dfn[m]);
    			write(u+v-m-m+1),putchar('
    ');
    		}
    	}
    	return 0;
    }
  • 相关阅读:
    python pandas里面的一些函数及用法
    Python enumerate() 函数
    论文笔记:EPTD模型/ Efficient and Privacy-Preserving Truth Discovery in Mobile Crowd Sensing Systems
    论文笔记:Adversarial Attacks and Defenses in Deep Learning 对抗训练部分
    一周入门Linux 基础篇 虚拟机快照
    一周入门Linux 基础篇 虚拟机克隆
    一周入门Linux 基础篇 网络连接的三种方式
    一周入门Linux 基础篇 安装vm和Centos
    B站考研网课推荐
    关于我
  • 原文地址:https://www.cnblogs.com/OYJason/p/9408297.html
Copyright © 2020-2023  润新知