洛谷 P1197 [JSOI2008]星球大戰(並查集+逆向)
阿新 • • 發佈:2018-12-08
題目描述
很久以前,在一個遙遠的星系,一個黑暗的帝國靠著它的超級武器統治著整個星系。
某一天,憑著一個偶然的機遇,一支反抗軍摧毀了帝國的超級武器,並攻下了星系中幾乎所有的星球。這些星球通過特殊的以太隧道互相直接或間接地連線。
但好景不長,很快帝國又重新造出了他的超級武器。憑藉這超級武器的力量,帝國開始有計劃地摧毀反抗軍佔領的星球。由於星球的不斷被摧毀,兩個星球之間的通訊通道也開始不可靠起來。
現在,反抗軍首領交給你一個任務:給出原來兩個星球之間的以太隧道連通情況以及帝國打擊的星球順序,以儘量快的速度求出每一次打擊之後反抗軍佔據的星球的連通塊的個數。(如果兩個星球可以通過現存的以太通道直接或間接地連通,則這兩個星球在同一個連通塊中)。
輸入輸出格式
輸入格式:
輸入檔案第一行包含兩個整數,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 行,每行一個整數,表示經過該次打擊後現存星球的連通塊個數。
思路:由於將點一個一個從並查集中拿去太難,所以逆向思考,將點一個一個加入並查集,倒序輸出。
#include<bits/stdc++.h> #define INF 0x3f3f3f3f #define mem(ar,num) memset(ar,num,sizeof(ar)) #define me(ar) memset(ar,0,sizeof(ar)) #define debug cout<<endl<<"DEBUG"<<endl; #define qx std::ios::sync_with_stdio(false) using namespace std; int d[400010],n,m,a,b,arr[400010],ans[400000],cnt,po[400000],k; int f(int x){return d[x] == x ? x : d[x] = f(d[x]);} vector<int> v[400010]; int main(){qx; cin>>n>>m; for(int i=1;i<n;i++)d[i]=i; for(int i=0;i<m;i++){ cin>>a>>b; v[a].push_back(b); v[b].push_back(a); } cin>>k; for(int i=0;i<k;i++){ cin>>po[i]; arr[po[i]]=1; } for(int i=0;i<n;i++){ if(arr[i])continue; for(int j=0;j<v[i].size();j++){ b=v[i][j]; if(arr[b])continue; int x=f(i),y=f(b); if(x!=y) d[x]=y; } } for(int i=0;i<n;i++){ if(d[i]==i&&!arr[i])ans[cnt]++; } int num=ans[cnt]; cnt++; for(int i=k-1;i>=0;i--){ arr[po[i]]=0;//剛剛修建了一個新的星球 num++;//多了一個點,所以聯通塊+1 for(int j=0;j<v[po[i]].size();j++){ if(arr[v[po[i]][j]]==0){//如果這裡也修建好了 b=v[po[i]][j]; int x=f(po[i]),y=f(b); if(x!=y){ d[x]=y; num--;//合併為一個聯通塊 } } } ans[cnt]=num; cnt++; } for(int i=cnt-1;i>=0;i--){ cout<<ans[i]<<endl; } return 0; }