BZOJ3637 Query on a tree VI

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3637

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

题目大意

询问树上同色联通块大小,支持单点反色。

题解

前置知识:维护虚子树大小

Q t r e e \mathcal{Qtree} Qtree系列感觉要被 A \mathcal{A} A成板子题了。。。

L C T \mathcal{LCT} LCT维护联通块(不知道是不是套路),建立黑白两棵 L C T \mathcal{LCT} LCT,当一个点在某一个 L C T \mathcal{LCT} LCT中被“连上”了,就说明这个点是这棵 L C T \mathcal{LCT} LCT的颜色。

注意,这里的“连上”仅仅指该点与原树中的父亲相连,而不是跟它的儿子相连。

为什么用这么奇怪的判定呢?

一个更简单粗暴的办法是,我们直接遍历所有边并砍掉,将其“彻底剥离” L C T \mathcal{LCT} LCT,这个也很好卡,菊花图即可。此时,使用上述判定的优势就显现出来了,因为一个节点指有一条指向父亲的边,这样每次修改我们就只用修改一条边,大大加快了效率。

那么询问如何处理呢?如果采用简单粗暴的办法,我们直接维护虚子树大小,查询时直接提出来就好了。其实我们可以使用相同的做法,特别之处在于,因为这颗子树的跟是不属于联通块的(由上面的判定可知,根节点没有连父边,所以颜色与该 L C T \mathcal{LCT} LCT不同),又因为 L C T \mathcal{LCT} LCT中的 S p l a y \mathcal{Splay} Splay维护的是深度严格单增的一条链,所以,我们只需要走到根节点的右儿子,输出其子树大小即可。

另外,值得注意的一点是,因为我们上述操作都用到了原树中的父子关系,所以我们必须定义有根树,而且 L C T \mathcal{LCT} LCT不能 m a k e r o o t \mathcal{makeroot} makeroot

代码

不知道为什么 B Z O J \mathcal{BZOJ} BZOJ这么卡常,反正在自家 O J \mathcal{OJ} OJ过了。

#include<bits/stdc++.h>
#define ls son[v][0]
#define rs son[v][1]
#define C col[a]
using namespace std;
const int M=1e6+5,N=M<<1;
int fa[N],n,m;
bool col[N];
vector<int>mmp[M];
struct LCT{
	int dad[N],son[N][2],siz[N],tot[N];
	LCT(){fill(tot+1,tot+N,1);}
	bool notroot(int v){return son[dad[v]][0]==v||son[dad[v]][1]==v;}
	void up(int v){tot[v]=tot[ls]+tot[rs]+siz[v]+1;}
	void spin(int v)
	{
		int f=dad[v],ff=dad[f],k=son[f][1]==v,w=son[v][!k];
		if(notroot(f))son[ff][son[ff][1]==f]=v;son[v][!k]=f,son[f][k]=w;
		if(w)dad[w]=f;dad[f]=v,dad[v]=ff;
		up(f);up(v);
	}
	void splay(int v)
	{
		int f,ff;
		while(notroot(v))
		{
			f=dad[v],ff=dad[f];
			if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);
			spin(v);
		}
	}
	void access(int v){for(int f=0;v;v=dad[f=v])splay(v),siz[v]+=tot[rs],siz[v]-=tot[rs=f];}
	int root(int v){access(v);splay(v);while(ls)v=ls;splay(v);return v;}
	void link(int v){access(v);splay(v);int f=dad[v]=fa[v];access(f);splay(f);siz[f]+=tot[v];tot[f]+=tot[v];}
	void cut(int v){access(v);splay(v);ls=dad[ls]=0;up(v);}
}lct[2];
void in()
{
	int a,b;scanf("%d",&n);
	for(int i=1;i<n;++i)scanf("%d%d",&a,&b),mmp[a].push_back(b),mmp[b].push_back(a);
}
void dfs(int v,int f)
{
	fa[v]=f;int to;
	for(int i=mmp[v].size()-1;i>=0;--i){to=mmp[v][i];if(to==f)continue;dfs(to,v);lct[0].link(to);}
}
void ac()
{
	int op,a,rt;
	dfs(1,n+1);lct[0].link(1);
	scanf("%d",&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&op,&a);
		if(op)lct[C].cut(a),lct[C^=1].link(a);
		else rt=lct[C].root(a),printf("%d\n",lct[C].tot[lct[C].son[rt][1]]);
	}
}
int main()
{
	in();ac();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值