BZOJ3637 Query on a tree VI

Description

You are given a tree (an acyclic undirected connected graph) with n nodes. The tree nodes are numbered from 1 to n.
Each node has a color, white or black. All the nodes are black initially.
We will ask you to perfrom some instructions of the following form:
0 u : ask for how many nodes are connected to u, two nodes are connected iff all the node on the path from u to v (inclusive u and v) have a same color.
1 u : toggle the color of u(that is, from black to white, or from white to black).

Input

The first line contains a number n denoted how many nodes in the tree(1 ≤ n ≤ 105). The next n - 1 lines, each line has two numbers (u,  v) describe a edge of the tree(1 ≤ u,  v ≤ n). The next line contains a number m denoted how many operations we are going to process(1 ≤ m ≤ 105). The next m lines, each line describe a operation (t,  u) as we mentioned above(0 ≤ t ≤ 1, 1 ≤ u ≤ n).

Output

For each query operation, output the corresponding result.

Sample Input

5

1 2

1 3

1 4

1 5

3

0 1

1 1

0 1

Sample Output

5

1

题目大意:单点修改颜色,求x统领的同色块的大小。

真不知道是怎么想到的,操作极骚= =

我们做两棵lct维护颜色森林,一棵维护黑色,一棵维护白色。

对于单点修改,在原lct中将x和fa[x]断开,在新颜色的lct中连接x和fa[x]。

对于查询,找到树根,如果c[root]=c[x],返回root的大小,否则返回右儿子的大小(右儿子为x)

理解做法后不难发现这种方法对于连通性及其适用,可以学习这种思想。只是我**真的不知道这做法怎么想出来的!

 

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 1e5+10;
int n,m,fa[N],col[N];
struct Splay{
	bool rev[N];
	int p[N],ch[N][2];
	int siz[N],ft[N];//总信息,虚子树信息(不包括自己) 
	#define Ls(v) ch[v][0]
	#define rs(v) ch[v][1]
	inline bool isroot(int x){
		return (Ls(p[x])^x)&&(rs(p[x])^x);
	}
	inline void pushdown(int x){
		if(rev[x]){
			rev[Ls(x)]^=1,rev[rs(x)]^=1;
			swap(Ls(x),rs(x));
			rev[x]=0;
		}
	}
	inline void maintain(int x){
		siz[x]=siz[Ls(x)]+siz[rs(x)]+ft[x]+1;
	}
	inline void rot(int x){
		int f=p[x],gf=p[f],type=rs(f)==x,son=ch[x][!type];
		if(!isroot(f))ch[gf][rs(gf)==f]=x;p[x]=gf;
		ch[p[son]=f][type]=son,maintain(f);
		ch[p[f]=x][!type]=f,maintain(x);
	}
	int top,stk[N];
	inline void splay(int x){
		stk[++top]=x;
		if(!isroot(x))for(int i=x;!isroot(i);i=p[i])stk[++top]=p[i];
		while(top)pushdown(stk[top--]);
		while(!isroot(x)){
			if(isroot(p[x]))return rot(x),void();
			if((rs(p[p[x]])==p[x])==(rs(p[x])==x))rot(p[x]);
			rot(x);
		}
	}
	inline int findrt(int x){
		while(Ls(x))x=Ls(x);
		return x;
	}
};
struct LCT{
	Splay sp;
	inline void access(int x){
		for(int lastx=0;x;lastx=x,x=sp.p[x])
			sp.splay(x),sp.ft[x]+=sp.siz[sp.rs(x)],sp.rs(x)=lastx,sp.ft[x]-=sp.siz[sp.rs(x)],sp.maintain(x);
	}
	inline void link(int x,int y){
		if(!x)return ;
		access(x),sp.splay(x);access(y),sp.splay(y);//因为是维护连通块,我们要将一整块拼上去 
		sp.p[y]=x,sp.ft[x]+=sp.siz[y],sp.maintain(x);
	}
	inline void cut(int x,int y){
		if(!x)return ;
		access(y),sp.splay(x);//access(y),y的儿子为x 
		sp.rs(x)=sp.p[y]=0,sp.maintain(x);
	}
}lct[2];
vector<int>G[N];
#define to G[x][i]
inline void dfs(int x){
	Inc(i,0,G[x].size()-1)if(to^fa[x]){
		lct[1].link(fa[to]=x,to);
		dfs(to);
	}
}
inline void init(){
	scanf("%d",&n);
	Inc(i,1,n)lct[1].sp.siz[i]=col[i]=1;//黑色
	Inc(i,1,n-1){
		int x,y;scanf("%d%d",&x,&y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
}
inline int Query(int x){
	lct[col[x]].access(x),lct[col[x]].sp.splay(x);
	int rt=lct[col[x]].sp.findrt(x);
	lct[col[x]].sp.splay(rt);
	if(col[rt]==col[x])return lct[col[x]].sp.siz[rt];
	else return lct[col[x]].sp.siz[lct[col[x]].sp.rs(rt)];
}
inline void solv(){
	scanf("%d",&m);
	while(m--){
		int op,x;scanf("%d%d",&op,&x);
		if(op==0)cout<<Query(x)<<"\n";
		else lct[col[x]].cut(fa[x],x),lct[col[x]^=1].link(fa[x],x);
	}
}
int main(){
	init();
	dfs(1);
	solv();
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值