BZOJ4551&&洛谷4092[HEOI/TJOI2016]树

中文题意不再赘述,说一下大体思路

网上流传的做法是并查集和树链剖分,我太弱只会并查集,这道题正常思路的并查集肯定做不了,因为要维护一个动态的关系,回想一下,之前我发过的一片博文亲戚,里面用到了并查集的离线处理,就是先将所有操作读入,需要修改的操作都做掉,然后从最后一个操作开始往回做,修改也就是说删边往回做的时候相当于加边,加边总比删边简单,所以离线倒序处理并查集就好,然后呢,这道题又有和一般并查集不一样的地方,我们需要把每个元素的父节点附成离他最近的一个元素,如何处理呢?dfs,如果当前点没有被标记,那么就把它的父节点赋为他父亲的父节点,用dfs遍历图,得到子树关系,如果当前点被标记,那么就把它的父节点附成本身,然后处理标记操作时,因为是逆序处理的操作,所以标记相当于取消标记,让它的标记--,如果它没有标记了,那么它的父节点就要变成dfs时,遍历到他的上一个点,就这么多,看代码

//By AcerMo
#include<map>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=100500;
int fa[M],siz[M],fatt[M];
int n,m,sign[M],ans[M];
vector<int>v[M];
struct que
{
	char s;
	int a;
}qlm[M];
int find(int x)
{
	if (x!=fa[x]) return fa[x]=find(fa[x]);
	return x;
}
void unionn(int a,int b)
{
	if (siz[a]<=siz[b]) siz[b]+=siz[a],fa[a]=b;
	else siz[a]+=siz[b],fa[b]=a;
	return ;
}
void built(int x)
{
	for (int i=0;i<v[x].size();i++)
	{
		int go=v[x][i];
		if (go!=fatt[x]) 
			fatt[go]=x,built(go);	
	} 
	return ;
}
int main() 
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<n;i++)
	{
		int a,b;scanf("%d%d",&a,&b);
		v[a].push_back(b);v[b].push_back(a);
	}
	built(1);sign[1]=1;
	for (int i=1;i<=m;i++)
	{
		scanf("%s %d",&qlm[i].s,&qlm[i].a);
		if (qlm[i].s=='C') sign[qlm[i].a]++;
	}
	for (int i=1;i<=n;i++) 
		if (sign[i]) fa[i]=i;
			else fa[i]=fatt[i];
	for (int i=m;i>0;i--)
	{
		if (qlm[i].s=='C') 
		{
			sign[qlm[i].a]--;
			if (!sign[qlm[i].a]) 
				fa[qlm[i].a]=fatt[qlm[i].a];
		}
		else ans[i]=find(qlm[i].a);
	}
	for (int i=1;i<=m;i++) 
		if (ans[i])
			cout<<ans[i]<<endl;
	return 0;
}


猜你喜欢

转载自blog.csdn.net/acerandaker/article/details/80719532