2020牛客暑期多校训练营(第三场)G Operating on a Graph 题解
题意
给一张n个点m条边的无向图,一开始 i 号点属于第 i 个集合,每次对一个集合进行操作,将与该集合中的点相连的点所处的集合归到该集合中,有无效操作,Q次操作过后询问每个点所处集合。
看到这道题首先想到的就是并查集,但是无法快速能够向集合外连边的点。
我们可以发现这样一个性质,一个点作为集合边上的一点向外延伸最多进行一次,所以我们每次A集合向B集合合并的时候由B集合向A集合连边,下次对B集合操作的时候就遍历与它连通的集合,然后看这些集合所代表的点是否能向外连边,因为每个点只会向外扩展一次,所以便利之后就可以将邻接表重置。最后复杂度O( n log n )
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<algorithm> 5 #include<cstring> 6 #define N 1000005 7 using namespace std; 8 int T,n,m; 9 int zz1,zz2,zz3,a1[N],a2[N],a3[N]; 10 struct ro{ 11 int to,next; 12 }road1[N*2],road2[N*2]; 13 void build1(int x,int y) 14 { 15 zz1++; 16 road1[zz1].to=y; 17 road1[zz1].next=a1[x]; 18 a1[x]=zz1; 19 } 20 void build2(int x,int y) 21 { 22 zz2++; 23 road2[zz2].to=y; 24 road2[zz2].next=a2[x]; 25 a2[x]=zz2; 26 } 27 bool fw[N]; 28 int fa[N],Q,tmp[N],cnt; 29 int find(int x) 30 { 31 if(fa[x]==x)return x; 32 return fa[x]=find(fa[x]); 33 } 34 void dfs(int x) 35 { 36 for(int i=a2[x];i;i=road2[i].next) 37 { 38 int y=road2[i].to; 39 dfs(y); 40 } 41 for(int i=a1[x];i;i=road1[i].next) 42 { 43 int y=road1[i].to; 44 if(find(y)!=find(x)&&!fw[find(y)]) 45 { 46 fw[find(y)]=1; 47 cnt++; 48 tmp[cnt]=find(y); 49 } 50 } 51 } 52 int main() 53 { 54 scanf("%d",&T); 55 while(T--) 56 { 57 scanf("%d%d",&n,&m); 58 zz1=0,zz2=0; 59 for(int i=1;i<=n;i++) fa[i]=i,a1[i]=a2[i]=0; 60 for(int i=1;i<=m;i++) 61 { 62 int x,y; 63 scanf("%d%d",&x,&y); 64 x++,y++; 65 build1(x,y); 66 build1(y,x); 67 } 68 scanf("%d",&Q); 69 cnt=0; 70 for(int i=1;i<=Q;i++) 71 { 72 int x; 73 scanf("%d",&x);x++; 74 if(find(x)!=x)continue; 75 dfs(x); 76 a2[x]=0; 77 for(int j=1;j<=cnt;j++) 78 { 79 fw[tmp[j]]=0; 80 build2(x,tmp[j]); 81 fa[tmp[j]]=x; 82 } 83 cnt=0; 84 } 85 for(int i=1;i<=n;i++) 86 { 87 printf("%d ",find(i)-1); 88 } 89 printf("\n"); 90 } 91 return 0; 92 }