[並查集] JSOI2008 星球大戰
題目描述
很久以前,在一個遙遠的星系,一個黑暗的帝國靠著它的超級武器統治者整個星系。
某一天,憑著一個偶然的機遇,一支反抗軍摧毀了帝國的超級武器,並攻下了星系中幾乎所有的星球。這些星球通過特殊的以太隧道互相直接或間接地連接。
但好景不長,很快帝國又重新造出了他的超級武器。憑借這超級武器的力量,帝國開始有計劃地摧毀反抗軍占領的星球。由於星球的不斷被摧毀,兩個星球之間的通訊通道也開始不可靠起來。
現在,反抗軍首領交給你一個任務:給出原來兩個星球之間的以太隧道連通情況以及帝國打擊的星球順序,以盡量快的速度求出每一次打擊之後反抗軍占據的星球的連通快的個數。(如果兩個星球可以通過現存的以太通道直接或間接地連通,則這兩個星球在同一個連通塊中)。
輸入輸出格式
輸入格式:
輸入文件第一行包含兩個整數, NN ( 1 < = N < = 2M ) 和 MM ( 1 < = M < = 200,000 ),分別表示星球的數目和以太隧道的數目。星球用 0 ~ N-1 的整數編號。
接下來的 M 行,每行包括兩個整數 X , Y ,其中( 0 < = X <> Y 表示星球 xx 和星球 yy 之間有 “以太” 隧道,可以直接通訊。
接下來的一行為一個整數 k ,表示將遭受攻擊的星球的數目。
接下來的 k 行,每行有一個整數,按照順序列出了帝國軍的攻擊目標。這 k 個數互不相同,且都在 0 到 n-1 的範圍內
輸出格式:
第一行是開始時星球的連通塊個數。接下來的 K 行,每行一個整數,表示經過該次打擊後現存星球的連通塊個數。
輸入輸出樣例
輸入樣例#1:
8 13
0 1
1 6
6 5
5 0
0 6
1 2
2 3
3 4
4 5
7 1
7 2
7 6
3 6
5
1
6
3
5
7
輸出樣例#1:
1
1
1
2
3
3
說明
[JSOI2008]
題解
也是一道用並查集的題目
看到題目描述中每次都會攻擊一個點,這個點及與其相連的邊都會消失,但是並查集並不支持刪除操作,怎麽辦呢?
我們可以倒序處理,首先把所有被攻擊的點打上標記,然後把所有點都掃一遍,把沒有標記的點建圖,當然這個圖不一定連通,也就是說可能剩下的點組成的集合不只一個連通塊,而這個連通快的數量就是k次攻擊後連通塊的數量,把它保存下來,再倒著處理,也就是for(i=k~1)把第i次刪除的點加上再建圖,再統計連通塊的數量,這就是第i-1攻擊後的答案,特別地當i=1時,ans[i-1]保存的就是最開始沒有受到攻擊時連通塊的數量
#include<bits/stdc++.h>
#define in(i) (i=read())
using namespace std;
int read()
{
int ans=0,f=1;
char i=getchar();
while(i<'0' || i>'9'){
if(i=='-') f=-1;
i=getchar();
}
while(i>='0' && i<='9'){
ans=(ans<<1)+(ans<<3)+i-'0';
i=getchar();
}
return ans*f;
}
int n,m,k;
int len;
int fa[400010];
struct edgec {
int from,to,next;
}e[4000010];
int head[400010];
int vis[400010],ans[400010],h[400010];
void add(int a,int b) {
e[++len].to=b;
e[len].from=a;
e[len].next=head[a];
head[a]=len;
}
int find(int x) {
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
int main()
{
in(n);in(m);
for(int i=0;i<n;i++ ) fa[i]=i;
for(int i=1;i<=m;i++) {
int a,b;
in(a);in(b);
add(a,b);
add(b,a);
}
in(k);
for(int i=1;i<=k;i++) {
int x;in(x);
vis[x]=1;
h[i]=x;
}
int tot=n-k;
for(int i=1;i<=2*m;i++) {
int x=e[i].from,y=e[i].to;
if(!vis[x] && !vis[y]) {
int fx=find(x),fy=find(y);
if(fx!=fy){
tot--;
fa[fx]=fy;
}
}
}
ans[k+1]=tot;
for(int t=k;t>=1;t--) {
int u=h[t];
vis[u]=0;
tot++;
for(int i=head[u];i;i=e[i].next) {
int to=e[i].to;
int x=find(u),y=find(to);
if(!vis[to] && x!=y) {
tot--;
fa[x]=y;
}
}
ans[t]=tot;
}
for(int i=1;i<=k+1;i++) {
cout<<ans[i]<<endl;
}
return 0;
}
[並查集] JSOI2008 星球大戰