【follow】BZOJ1015: [JSOI2008]星球大戰starwar
阿新 • • 發佈:2019-02-03
意思是給一個圖,每次刪除一個點和與其相關的連邊,每次求出連通塊的個數。
這道題按照題目的說法,很不好想。但是如果把題目倒過來,我們只需要每次在圖中新增節點,並判斷新增的節點是否:{產生新的連通塊,將兩個已有連通塊連在一起}即可。
這就是並查集的思想。
首先我們建一張參照圖,建立題中的所有邊。
用ans表示現在的連通塊個數,初始為0即可。
然後統計出那些點沒有被踢出(用ove來表示),建立一張新的圖。統計這個圖的聯通塊數量,存為ans,存入最終輸出的答案陣列中。
逆序新增點,每次新增,更新ans,存入最後輸出的答案陣列中。
最後逆序輸出答案陣列即可。
[更新操作]insert
首先,此點產生了一個新的連通塊,ans++. (*)
我們加入了一個點。首先要按照原圖,尋找相連的邊,如果此邊通向的點在圖中,並且與加入的點不在一個連通塊(用並查集即可),那麼ans--,並連線這兩個連通塊。
不在一個連通塊的話就不用管了。(因為已經ans++了。(*處))
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; const int maxn=400010; int fa[maxn],head[maxn],cnt,d[maxn],gnt; bool ove[maxn],use[maxn]; int n,m,k,sum[maxn],ans; struct edge { int nxt,to; }e[maxn],g[maxn]; void add1(int x,int y)//對照組(所有邊) { e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt; } void add2(int x,int y)//正式組(被攻打之後) { g[++gnt].to=y; g[gnt].nxt=head[x]; head[x]=gnt; } int find(int x) { if(fa[x]==x)return x; return fa[x]=find(fa[x]); } void insert(int x) { for(int i=head[x];i;i=e[i].nxt) if(use[e[i].to]==1)//在圖中 { add2(x,e[i].to); if(find(e[i].to)!=find(x)) { ans--; fa[find(x)]=find(e[i].to); } } } int main() { cin>>n>>m; for(int i=1;i<=n;i++)fa[i]=i; int x,y; for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); x++,y++; add1(x,y); add1(y,x); } cin>>k; for(int i=1;i<=k;i++) { scanf("%d",&d[i]); d[i]++; ove[d[i]]=1; } for(int i=1;i<=n;i++) if(ove[i]==0) { insert(i); ans++; use[i]=1; } sum[k+1]=ans; for(int i=k;i>=1;i--) { insert(d[i]); ans++; use[d[i]]=1; sum[i]=ans; } for(int i=1;i<=k+1;i++)printf("%d\n",sum[i]); return 0; }