zoukankan      html  css  js  c++  java
  • BZOJ4817: [Sdoi2017]树点涂色

    BZOJ4817: [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操作,输出一个数表示权值的最大值

    Sample Input

    5 6
    1 2
    2 3
    3 4
    3 5
    2 4 5
    3 3
    1 4
    2 4 5
    1 5
    2 4 5

    Sample Output

    3
    4
    2
    2
    题解Here!

    看到题目第一想法:树剖。

    但是1操作要改一颗子树,还要分 在链上 与 不在链上 两种,可能还有树上分治什么乱七八糟的玩意,时间+空间+思考+代码 复杂度均不能接受。。。

    正解:Link-Cut-Tree。。。

    先构出原树的虚树,并用树剖维护每个点到根节点的路径权值data,那么每个点到根节点的路径权值 data 就是每个点到根节点的路径上 实链 的个数。

    于是1操作就变成了access:

    如果一条实边变成虚边,那么将连接这条边的深度较大的节点的子树里所有点的 data 加 1 (因为实链数量就等于虚边数量+1 );

    如果一条虚边变成实边,那么将连接这条边的深度较大的节点的子树里所有点的 data 减 1 。

    因为没有加删边,故2、3操作还是用 树剖+线段树 维护。

    2操作:

    ans=data[x]+data[y]-data[LCA(x,y)]*2+1;

    3操作:

    ans=query_max(id[x],id[x]+size[x]-1);

    代码中为了防止 线段树 与 LCT 混淆,用了 namespace 封装。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 100010
    using namespace std;
    int n,m,c=1,d=1;
    int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN];
    struct node{
    	int next,to;
    }a[MAXN<<1];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    namespace ST{
    	#define LSON rt<<1
    	#define RSON rt<<1|1
    	#define DATA(x) a[x].data
    	#define SIGN(x) a[x].c
    	#define LSIDE(x) a[x].l
    	#define RSIDE(x) a[x].r
    	struct Segment_Tree{
    		int data,c,l,r;
    	}a[MAXN<<2];
    	inline void pushup(int rt){
    		DATA(rt)=max(DATA(LSON),DATA(RSON));
    	}
    	inline void pushdown(int rt){
    		if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return;
    		SIGN(LSON)+=SIGN(rt);DATA(LSON)+=SIGN(rt);
    		SIGN(RSON)+=SIGN(rt);DATA(RSON)+=SIGN(rt);
    		SIGN(rt)=0;
    	}
    	void buildtree(int l,int r,int rt){
    		int mid;
    		LSIDE(rt)=l;
    		RSIDE(rt)=r;
    		if(l==r){
    			DATA(rt)=deep[pos[l]];
    			return;
    		}
    		mid=l+r>>1;
    		buildtree(l,mid,LSON);
    		buildtree(mid+1,r,RSON);
    		pushup(rt);
    	}
    	void update(int l,int r,int c,int rt){
    		int mid;
    		if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
    			SIGN(rt)+=c;DATA(rt)+=c;
    			return;
    		}
    		pushdown(rt);
    		mid=LSIDE(rt)+RSIDE(rt)>>1;
    		if(l<=mid)update(l,r,c,LSON);
    		if(mid<r)update(l,r,c,RSON);
    		pushup(rt);
    	}
    	int query(int l,int r,int rt){
    		int mid,ans=0;
    		if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
    		pushdown(rt);
    		mid=LSIDE(rt)+RSIDE(rt)>>1;
    		if(l<=mid)ans=max(ans,query(l,r,LSON));
    		if(mid<r)ans=max(ans,query(l,r,RSON));
    		return ans;
    	}
    }
    namespace LCT{
    	int top,stack[MAXN];
    	struct Link_Cut_Tree{
    		int f,flag,son[2];
    		int v;
    	}a[MAXN];
    	inline bool isroot(int rt){
    		return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
    	}
    	inline void pushup(int rt){
    		if(!rt)return;
    		if(a[rt].son[0])a[rt].v=a[a[rt].son[0]].v;
    		else a[rt].v=rt;
    	}
    	inline void pushdown(int rt){
    		if(!rt||!a[rt].flag)return;
    		a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1;
    		swap(a[rt].son[0],a[rt].son[1]);
    	}
    	inline void turn(int rt){
    		int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
    		if(!isroot(x)){
    			if(a[y].son[0]==x)a[y].son[0]=rt;
    			else a[y].son[1]=rt;
    		}
    		a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
    		a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
    		pushup(x);pushup(rt);
    	}
    	void splay(int rt){
    		top=0;
    		stack[++top]=rt;
    		for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
    		while(top)pushdown(stack[top--]);
    		while(!isroot(rt)){
    			int x=a[rt].f,y=a[x].f;
    			if(!isroot(x)){
    				if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
    				else turn(x);
    			}
    			turn(rt);
    		}
    	}
    	void access(int rt){
    		for(int i=0;rt;i=rt,rt=a[rt].f){
    			splay(rt);
    			if(a[rt].son[1])ST::update(id[a[a[rt].son[1]].v],id[a[a[rt].son[1]].v]+size[a[a[rt].son[1]].v]-1,1,1);
    			if(i)ST::update(id[a[i].v],id[a[i].v]+size[a[i].v]-1,-1,1);
    			a[rt].son[1]=i;
    			if(i)a[i].f=rt;
    		}
    	}
    }
    inline void add(int x,int y){
    	a[c].to=y;a[c].next=head[x];head[x]=c++;
    	a[c].to=x;a[c].next=head[y];head[y]=c++;
    }
    void dfs1(int rt){
    	son[rt]=0;size[rt]=1;
    	for(int i=head[rt];i;i=a[i].next){
    		int will=a[i].to;
    		if(!deep[will]){
    			deep[will]=deep[rt]+1;
    			fa[will]=rt;
    			dfs1(will);
    			size[rt]+=size[will];
    			if(size[son[rt]]<size[will])son[rt]=will;
    		}
    	}
    }
    void dfs2(int rt,int f){
    	id[rt]=d++;pos[d-1]=rt;top[rt]=f;
    	if(son[rt])dfs2(son[rt],f);
    	for(int i=head[rt];i;i=a[i].next){
    		int will=a[i].to;
    		if(will!=fa[rt]&&will!=son[rt])
    		dfs2(will,will);
    	}
    }
    int LCA(int x,int y){
    	while(top[x]!=top[y]){
    		if(deep[top[x]]<deep[top[y]])swap(x,y);
    		x=fa[top[x]];
    	}
    	if(deep[x]>deep[y])swap(x,y);
    	return x;
    }
    void work(){
    	int f,x,y;
    	while(m--){
    		f=read();x=read();
    		if(f==1)LCT::access(x);
    		if(f==2){
    			y=read();
    			int fa=LCA(x,y);
    			printf("%d
    ",(ST::query(id[x],id[x],1)+ST::query(id[y],id[y],1)-ST::query(id[fa],id[fa],1)*2+1));
    		}
    		if(f==3)printf("%d
    ",ST::query(id[x],id[x]+size[x]-1,1));
    	}
    }
    void init(){
    	int x,y;
    	n=read();m=read();
    	for(int i=1;i<n;i++){
    		x=read();y=read();
    		add(x,y);
    	}
    	deep[1]=1;
    	dfs1(1);
    	dfs2(1,1);
    	ST::buildtree(1,n,1);
    	for(int i=1;i<=n;i++){
    		LCT::a[i].f=fa[i];
    		LCT::a[i].v=i;
    	}
    }
    int main(){
    	init();
    	work();
    	return 0;
    }
    
  • 相关阅读:
    CSS伪元素:before/CSS伪元素:before/:after content 显示Font Awesome字体图标:after content 显示Font Awesome字体图标
    window.requestAnimationFrame
    HTML5 canvas clearRect() 方法
    canvas save()和canvas restore()状态的保存和恢复使用方法及实例
    git -分支管理(创建、推送、删除)
    微信小程序设置域名、不校验域名
    微信小程序访问豆瓣api403问题解决方发法
    干货:Vue粒子特效(vue-particles插件)
    chmod命令详细用法
    Python新式类和旧式类的区别
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9097267.html
Copyright © 2011-2022 走看看