bzoj P1015 [JSOI2008]星球大戰starwar(luogu P1197)
阿新 • • 發佈:2018-12-12
演算法:並查集
難度:水題(NOIP-)
這道題很容易看出來要用到並查集,但是由於無法做到在並查集中刪點,所以我們選擇反著搞,將刪點改為加點,然後就很輕鬆的可以搞“並查集求聯通塊個數”了,
注意:陣列不要開小了!N (1 < = N < = 2M) 和M (1 < = M < = 200,000)
程式碼如下:
#include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <algorithm> #include <cstdlib> #include <queue> #define N 200002 using namespace std; int head[N<<1];//N<=2*M,!陣列開小了 struct node { int next; int to; }edge[N<<2]; int cnt=1; void init() { cnt=1; memset(head,-1,sizeof(head)); } void add(int u,int v) { edge[cnt].next=head[u]; edge[cnt].to=v; head[u]=cnt++; } int fa[N<<1],del[N<<1]; int findf(int x) { if(x==fa[x]) return x; return fa[x]=findf(fa[x]); } int vis[N<<1]; int ans[N<<1]; int main() { int n,m; scanf("%d%d",&n,&m); //read(n),read(m); init(); for(int i = 1;i <= n+1;i++) { fa[i]=i; } for(int i = 1;i <= m;i++) { int u,v; scanf("%d%d",&u,&v); //read(u),read(v); ++u,++v; add(u,v),add(v,u); } int k; scanf("%d",&k); //read(k); for(int i = 1;i <= k;i++) { scanf("%d",&del[i]); //read(del[i]); ++del[i]; vis[del[i]]=1; } int sum=n; for(int i = 1;i <= n;i++) { if(vis[i]) continue; for(int j = head[i];j!=-1;j=edge[j].next) { int to=edge[j].to; if(vis[to]) continue; int t1=findf(i);//起點是i,不是j!!! int t2=findf(to); if(t1==t2) continue; else { fa[t2]=t1; sum--; } } } for(int i = k;i >= 1;i--) { int d=del[i]; ans[i]=sum-i; vis[d]=0; for(int j = head[d];j!=-1;j=edge[j].next)//都是j,沒有i!!! { int to=edge[j].to; if(vis[to]) continue; int t1=findf(d);//起點是d,不是j!!! int t2=findf(to); if(t1==t2) continue; else { fa[t2]=t1; sum--; } } } printf("%d\n",sum); for(int i = 1;i <= k;i++) { printf("%d\n",ans[i]); } return 0 ; }