P1197 [JSOI2008]星球大战
题意
n个星球,m条边,k此摧毁,每次都会摧毁一个星球,摧毁一次求一次联通块,没摧毁时也要输出一次
解法
求联通块有两种方法
- 利用dfs或者bfs从每一个点出发,把能到达的点都标记,每一个点都走一次,时间复杂度为O(n),
- 使用并查集,单独n个点联通块就是n个,当两个点连接起来并且这两个点分别属于两个不同的联通块时,把他们合并成一个集合,联通块数量-1,时间复杂度差不多等于边的数量,>O(m)
如果只是求一次联通块的数量显然第一种方法更优,但是这道题每少一个点就要求一次联通块,如果用第一种方法时复O(kn),这道题会超时,这时如果用并查集正着来做,时复O(mk),也会超时,但是换一种思路,这道题要摧毁星球,我们倒着来,修建星球,每修建一个求一次联通块,而这时并查集时复就是常数级别O(max(m,k)),每修建一个多了一个点,联通块+1,看看和这个点相连的点是否属于一个集合,属于就-1,一个pair存点,再用链式前向星存一下图
CODE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| #include <bits/stdc++.h> #define debug freopen("in.txt","r",stdin); freopen("out.txt","w",stdout) #define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0) using namespace std; typedef long long ll; typedef unsigned long long ull; const int MAXN=1e6+100; const double pi=acos(-1); const int MOD=1e9+7; const int INF=0x3f3f3f3f; const int SUB=-0x3f3f3f3f; const int eps=1e-4; struct node{ int to,next; }e[MAXN]; int fa[MAXN],ans[MAXN],head[MAXN],dead[MAXN]; bool vis[MAXN]; pair<int,int> b[MAXN]; int tot,n,m,k; void add(int u,int v){ e[tot]={v,head[u]}; head[u]=tot++; } int find(int x){ if(x==fa[x]) return x; return fa[x]=find(fa[x]); } void remerge(int x,int y){ int fx=find(x),fy=find(y); if(fx!=fy) fa[fx]=fy; } int main(){ ios; cin>>n>>m; for(int i=0;i<=n;i++) fa[i]=i; memset(head,-1,sizeof head); for(int i=1;i<=m;i++){ int u,v; cin>>u>>v; b[i]={u,v}; add(u,v); add(v,u); } cin>>k; for(int i=1;i<=k;i++){ cin>>dead[i]; vis[dead[i]]=1; } int num=n-k; for(int i=1;i<=m;i++){ if(!vis[b[i].first] && !vis[b[i].second] && find(b[i].first)!=find(b[i].second)){ remerge(b[i].first,b[i].second); num--; } } ans[0]=num; for(int i=k;i>=1;i--){ num++; vis[dead[i]]=0; for(int j=head[dead[i]];~j;j=e[j].next){ int v=e[j].to; if(!vis[v] && find(v)!=find(dead[i])){ num--; remerge(v,dead[i]); } } ans[k-i+1]=num; } for(int i=k;i>=0;i--) cout<<ans[i]<<'\n'; return 0; }
|
利用dfs求:
20分,其中一个地方爆栈了,可以换成bfs来求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <bits/stdc++.h> #define debug freopen("in.txt","r",stdin); freopen("out.txt","w",stdout) #define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0) using namespace std; typedef long long ll; typedef unsigned long long ull; const int MAXN=400010; const double pi=acos(-1); const int MOD=1e9+7; const int INF=0x3f3f3f3f; const int SUB=-0x3f3f3f3f; const int eps=1e-4; struct node{ int to,next; }e[200010]; int n,m,tot,k,x,y,js; int head[MAXN]; bool vis[MAXN],used[MAXN],book[MAXN]; void add(int u,int v){ e[tot]={v,head[u]}; head[u]=tot++; } void dfs(int x){ vis[x]=1; for(int i=head[x];~i;i=e[i].next){ int v=e[i].to; if(vis[v] || used[v]) continue; dfs(v); } } int main(){ ios; memset(head,-1,sizeof head); cin>>n>>m; while(m--){ cin>>x>>y; add(x,y); add(y,x); } cin>>k; for(int i=0;i<=n-1;i++){ if(vis[i] || used[i]) continue; js++; dfs(i); } cout<<js<<'\n'; while(k--){ memset(vis,0,sizeof vis); js=0; cin>>x; used[x]=1; for(int i=0;i<=n-1;i++){ if(vis[i] || used[i]) continue; js++; dfs(i); } cout<<js<<'\n'; } return 0; }
|