hdu5923 Prediction(并查集/可继承的并查集+并查集合并)

题目

n(n<=500)个点,m(m<=1e4)条边,代表一个图G

以下m-1条边,生成一棵m个节点的魔法树

以下m条边,第i条边为树上的第i个节点所保存,

q(q<=5e4)个询问,

每次给出k个节点,释放这些节点本身保存的边,

也释放这些节点在魔法树上的祖先节点保存的边,

这些边和原图G的所有节点构成一个新图G’

对于每个询问,求G’的联通块的个数

数据保证,对于每个样例,所有k之和<=3e5

思路来源

https://blog.csdn.net/a664607530/article/details/74144263

https://blog.csdn.net/fnoi11awyfeng/article/details/81951160

题解

对于树上的每个节点,开一个并查集,

u所在的并查集维护的是,只考虑u这个节点,图G’的连通关系是怎样的

那么u实际上考虑的是其到根的这条链上所有节点,所以需要继承其父亲的连通关系

在dfs预处理时,处理出u的关系,并将u所保存的边释放,

 

对于每个查询,暴力地将每个点u所在的并查集关系合并成一个并查集,

实际的操作是,遍历原图G的每一个节点i,

如果i在树上节点u的这个图里的祖先是j,说明在u这个图里i和j是连通的,

那么在总的图里,i和j就一定是连通的,在总的并查集里合并i和j即可(如果i不等于j)

总的并查集,在代码里体现为0号节点维护的并查集

代码复杂度应该是O(T*\sum_{i=1}^{q}k_{i}*n),大概单组1.5e8叭,具体也不知如何

代码

#include 
#include 
#include 
#include 
#include  
#include  
using namespace std;
typedef pair P;
const int maxn=505;	
const int maxm=1e4+5;
//原图有n个点m条边 但是新树有m个点 
int T,n,m,q;
int u,v,num;
int ans;
vectorE[maxm];//新树每个节点存的儿子 
P edge[maxm];//新树每个节点屯的边 
struct DSU
{
	int par[maxn];//原图的连通关系 
	void init(int n=500)
	{
		for(int i=1;i<=n;++i)
		par[i]=i;	
	}
	void copy(const DSU &b)
	{
		for(int i=1;i<=n;++i)
		par[i]=b.par[i];
	}
	int find(int x)
	{
		return par[x]==x?x:par[x]=find(par[x]);
	}
	void unite(int x,int y)
	{
		x=find(x),y=find(y);
		if(x==y)return;
		par[x]=y;
	}
}e[maxm];//新树里,每个节点建一个并查集 
void dfs(int u,int fa)
{
	e[u].copy(e[fa]);//可继承并查集 
	e[u].unite(edge[u].first,edge[u].second);//释放u藏的边将其合并 
	for(int i=0;i

 

你可能感兴趣的:(树/并查集)