1015: [JSOI2008]星球大戰starwar
Time Limit: 3 Sec Memory Limit: 162 MBSubmit: 5253 Solved: 2395
[Submit][Status][Discuss]
Description
很久以前,在一個遙遠的星系,一個黑暗的帝國靠著它的超級武器統治者整個星系。某一天,憑著一個偶然的
機遇,一支反抗軍摧毀了帝國的超級武器,並攻下了星系中幾乎所有的星球。這些星球通過特殊的以太隧道互相直
接或間接地連線。 但好景不長,很快帝國又重新造出了他的超級武器。憑藉這超級武器的力量,帝國開始有計劃
地摧毀反抗軍佔領的星球。由於星球的不斷被摧毀,兩個星球之間的通訊通道也開始不可靠起來。現在,反抗軍首
領交給你一個任務:給出原來兩個星球之間的以太隧道連通情況以及帝國打擊的星球順序,以儘量快的速度求出每
一次打擊之後反抗軍佔據的星球的連通快的個數。(如果兩個星球可以通過現存的以太通道直接或間接地連通,則
這兩個星球在同一個連通塊中)。
Input
輸入檔案第一行包含兩個整數,N (1 < = N < = 2M) 和M (1 < = M < = 200,000),分別表示星球的
數目和以太隧道的數目。星球用 0 ~ N-1的整數編號。接下來的M行,每行包括兩個整數X, Y,其中(0 < = X <>
Y 表示星球x和星球y之間有“以太”隧道,可以直接通訊。接下來的一行為一個整數k,表示將遭受攻擊的星球的
數目。接下來的k行,每行有一個整數,按照順序列出了帝國軍的攻擊目標。這k個數互不相同,且都在0到n-1的範
圍內。
Output
輸出檔案的第一行是開始時星球的連通塊個數。接下來的N行,每行一個整數,表示經過該次打擊後現存星球
的連通塊個數。
Sample Input
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
Sample Output
1
1
2
3
3
並查集不支援刪除,所以倒著處理,每次加入一個點
用tot記錄連通塊個數,沒加一個點就+1,遇到兩個合併就-1,不會被破壞的點也用加的方法
加一個點是要便利它的所有邊
/************************************************************** Problem: 1015 User: thwfhk Language: C++ Result: Accepted Time:1284 ms Memory:13788 kb ****************************************************************/ // // main.cpp // bzoj1015 // // Created by Candy on 9/17/16. // Copyright © 2016 Candy. All rights reserved. // #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; const int N=4e5+5,M=2e5+5; int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } struct edge{ int v,ne; }e[M<<1]; int h[N],cnt=0; inline void ins(int u,int v){ cnt++; e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt; cnt++; e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt; } int n,m,x,y,k,tot=0,ans[N]; int has[N],lst[N],vis[N];//vis for ins from 0 int fa[N]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} inline void merge(int x,int y){ int f1=find(x),f2=find(y); if(f1!=f2) fa[f1]=f2,tot--; } void add(int u){ for(int i=h[u];i;i=e[i].ne){ int v=e[i].v; if(vis[v]) merge(u,v); } } int main(int argc, const char * argv[]) { n=read();m=read(); for(int i=1;i<=m;i++){ x=read()+1;y=read()+1;ins(x,y); } k=read(); for(int i=1;i<=n;i++) has[i]=1,fa[i]=i; for(int i=1;i<=k;i++){ x=read()+1; has[x]=0; lst[i]=x; } for(int u=1;u<=n;u++) if(has[u]){ tot++; add(u); vis[u]=1; } ans[k+1]=tot; for(int i=k;i>=1;i--){ tot++; add(lst[i]); vis[lst[i]]=1; ans[i]=tot; } for(int i=1;i<=k+1;i++) printf("%d\n",ans[i]); return 0; }
[2016-11-15]
注意一開始也要求一下cc個數
加入一個點可能連起好多個cc來,用並查集維護連通性
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int N=4e5+5,M=2e5+5,INF=1e9; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int n,m,k,a[N],des[N],x,y,ans[N]; struct edge{ int v,w,ne; }e[M<<1]; int h[N],cnt=0; inline void ins(int u,int v){ cnt++; e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt; cnt++; e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt; } int fa[N]; inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} int tot=0; inline void unn(int x,int y){ int f1=find(x),f2=find(y); if(f1!=f2) fa[f1]=f2,tot--; } void add(int u){ for(int i=h[u];i;i=e[i].ne){ int v=e[i].v; if(!des[v]) unn(u,v); } } int main(){ n=read();m=read(); for(int i=1;i<=m;i++){x=read()+1;y=read()+1;ins(x,y);} k=read(); for(int i=1;i<=k;i++) a[i]=read()+1,des[a[i]]=1; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=n;i++) if(!des[i]){ tot++; add(i); } ans[k+1]=tot; for(int i=k;i>=1;i--){ tot++; des[a[i]]=0; add(a[i]); ans[i]=tot; } for(int i=1;i<=k+1;i++) printf("%d\n",ans[i]); }