题目大意:给你n个点,m条边,现在有q次操作,每次一个x,表示将与x所连的所有点归并到x团里面,问最后每个点属于哪个团。
输入
5 4 3 0 1 1 2 2 3 4 0 1 3 0 4 3 0 1 1 2 2 3 2 0 2 4 3 0 1 1 2 2 3 2 0 3 4 1 1 3 1 2 5 5 0 1 0 2 1 2 1 3 3 4 3 4 4 0
输出
0 0 0 0 2 2 2 2 0 0 3 3 0 1 2 3 0 0 0 0 0
看见团和合并之类的,很容易联想到并查集,每次合并的时候我们将每个点的祖宗设置为x就好了(注意,如果x的祖宗不是自己的话就说明它已经被合并了,这个x团中就是空的,我们直接跳过)。接下来就是将两个祖宗团的所有点合并了,我们需要用一个数据结构来实现这种操作,而list应该是一个比较理想的容器,它的splice方法是比较高效的
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=1e6+10; struct Edge { int to,next; }eg[mac<<1]; int head[mac],father[mac],num=0; list<int>group[mac]; bool vis[mac]; void add(int u,int v) { eg[++num]=Edge{v,head[u]}; head[u]=num; } int find(int x){return x==father[x]?x:father[x]=find(father[x]);} void unions(int u,int v) { int fu=find(u),fv=find(v); if (fu!=fv){ father[fu]=fv; group[fv].splice(group[fv].end(),group[fu]); } } int main(int argc, char const *argv[]) { int t,n,m; scanf ("%d",&t); while (t--){ scanf ("%d%d",&n,&m); num=0; for (int i=0; i<n+10; i++){ head[i]=-1;father[i]=i; vis[i]=0; group[i].clear(); group[i].push_back(i); } for (int i=1; i<=m; i++){ int u,v; scanf ("%d%d",&u,&v); add(u,v);add(v,u); } int q; scanf ("%d",&q); while (q--){ int x; scanf ("%d",&x); int gkd=find(x); if (gkd!=x) continue; int len=group[x].size(); for (int i=0; i<len; i++){//!!注意这里不能用size。 int u=group[x].front(); for (int j=head[u]; j!=-1; j=eg[j].next){ int v=eg[j].to; unions(v,x); if (vis[v]) continue; group[x].push_back(v); vis[v]=1; } group[x].pop_front(); } } for (int i=0; i<n; i++) printf("%d ",find(i)); printf("\n"); } return 0; }