【JSOI2008】星球大战 并查集
题目描述
很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。
某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。
但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。
现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。
输入输出格式
输入格式:
输入文件第一行包含两个整数,NN (1 < = N < = 2M1<=N<=2M) 和 MM (1 < = M < = 200,0001<=M<=200,000),分别表示星球的数目和以太隧道的数目。星球用 00 ~ N-1N−1 的整数编号。
接下来的 MM 行,每行包括两个整数 XX, YY,其中( 0 < = X <> Y0<=X<>Y 表示星球 xx 和星球 yy 之间有 “以太” 隧道,可以直接通讯。
接下来的一行为一个整数 kk ,表示将遭受攻击的星球的数目。
接下来的 kk 行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这 kk 个数互不相同,且都在 00 到 n-1n−1的范围内。
输出格式:
第一行是开始时星球的连通块个数。接下来的 KK 行,每行一个整数,表示经过该次打击后现存星球的连通块个数。
输入输出样例
输出样例#1:
1
要看吗ovo
1 2 3 3
---------------------------------------------------
好的这道题一眼是个并查集(其实是按专题在做qwq 遇到的是一个常用但我没见过的套路
题目要求查询的是依次毁灭后的情形 但如果真的要顺向查找连通块确实有点过于暴力
这时候 我们要做的是反向建立图并完成并查集操作
有一个细节是:要将最后一个点先单独操作 其余点在一遍循环中操作 这样比较好记答案 而且不能随意将最后一步的点直接vis[u]=0;
算法离线 每一次取最后被ban掉的点 将与之相连的边和点插入并查集中合并 同时计算连通块的个数
怎么计算连通块的个数呢?
蒟蒻的我只想到了暴力每次求不同的祖先数
聪明的办法是:每加入一个新点(之前被ban掉的)时,连通块数量++ ;遍历时每发现一个没有在并查集中 但与当前点有边相连 的点,合并之后连通块数量--;
这样就ok了
像这样在处理过程中就进行答案累积的类似的思路还有计算最短路条数时,如果在最短路上 则ans[v]=ans[u]; 否则如果dis[v]==dis[u]+1 ans[v]=ans[u]+1;
千万不要每次一想到记答案就要重头遍历啊!
贴个代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #define N 401100 7 using namespace std; 8 int n,m,k,tot; 9 struct node 10 { 11 int u,v,w,nxt; 12 }e[N*2]; 13 int first[N],cnt; 14 void ade(int u,int v) 15 { 16 e[++cnt].nxt=first[u]; first[u]=cnt; 17 e[cnt].u=u; e[cnt].v=v; 18 } 19 int fa[N*2]; 20 int la(int x) 21 { 22 if(fa[x]!=x) return fa[x]=la(fa[x]); 23 else return fa[x]; 24 } 25 void combine(int x,int y) 26 { 27 int xx=la(x),yy=la(y); 28 if(xx!=yy) 29 { 30 fa[xx]=yy; tot--; 31 } 32 } 33 int ak[N*2]; 34 bool vis[N*2]; 35 int col[N*2]; 36 int main() 37 { 38 scanf("%d%d",&n,&m); 39 for(int i=1,x,y;i<=m;i++) 40 { 41 scanf("%d%d",&x,&y); 42 ade(x+1,y+1); ade(y+1,x+1); 43 } 44 for(int i=1;i<=n;i++) fa[i]=i; 45 scanf("%d",&k); 46 for(int i=1;i<=k;i++) 47 { 48 scanf("%d",&ak[i]); 49 ak[i]++; 50 vis[ak[i]]=1; 51 } 52 tot=n-k; 53 for(int u=1;u<=n;u++) 54 { 55 if(vis[u]) continue; 56 for(int i=first[u];i;i=e[i].nxt) 57 { 58 int v=e[i].v; 59 if(vis[v]) continue; 60 combine(u,v); 61 } 62 } 63 col[k+1]=tot; 64 for(int pu=k;pu>=1;pu--) 65 { 66 int u=ak[pu]; 67 vis[u]=0; 68 tot++; 69 for(int i=first[u];i;i=e[i].nxt) 70 { 71 int v=e[i].v; 72 if(vis[v]) continue; 73 combine(u,v); 74 } 75 col[pu]=tot; 76 } 77 for(int i=1;i<=k+1;i++) 78 printf("%d\n",col[i]); 79 return 0; 80 }