题目
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号节点维护的并查集
代码复杂度应该是,大概单组1.5e8叭,具体也不知如何
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int,int> P;
const int maxn=505;
const int maxm=1e4+5;
//原图有n个点m条边 但是新树有m个点
int T,n,m,q;
int u,v,num;
int ans;
vector<int>E[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<E[u].size();++i)
{
int v=E[u][i];
dfs(v,u);
}
}
int main()
{
scanf("%d",&T);
for(int cas=1;cas<=T;++cas)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)//新树有m个点
E[i].clear();
for(int i=1;i<m;++i)
{
scanf("%d",&u);
E[u].push_back(i+1);
}
for(int i=1;i<=m;++i)
{
scanf("%d%d",&u,&v);
edge[i]=P(u,v);//每个点可以释放一条边
}
e[0].init(n);
dfs(1,0);
printf("Case #%d:\n",cas);
scanf("%d",&q);
while(q--)
{
ans=0;
e[0].init(n);
scanf("%d",&num);
for(int i=1;i<=num;++i)
{
scanf("%d",&u);//在u所在的链并查集里
for(int j=1;j<=n;++j)
{
int fa=e[u].find(j);//在u所在并查集里,fa和j在同一个集合
if(fa!=j)e[0].unite(fa,j);//所以,在0这个并查集里,fa和j也该在同一个集合里
}
}
for(int i=1;i<=n;++i)
if(e[0].find(i)==i)ans++;
printf("%d\n",ans);
}
}
return 0;
}