题目描述
燃烧军团大举入侵艾泽拉斯,现以在艾星建立了大量的军事要塞,这些要塞通过若干个道路直接或间接连接。如果两个城市是连通的,那么它们处于同一连通块之中。现在抗魔联军发明了一种威力巨大的法术,每发动一次将会直接摧毁一个军事要塞,同时也摧毁与他相连的道路。每进行一次打击,联军需要知道未被摧毁的要塞的连通块数。联军会给出最初要塞的连通情况以及打击顺序,请快速计算每次打击之后未被摧毁的要塞的连通块数量。 输入
输入第一行为两个整数n,m分别表示要塞个数和道路个数。1<=n<=100000,1<=m<=200000。 接下来m行每行两个整数x,y (1<=x,y<=n)表示要塞x和要塞y之间有一条道路连接。 接下来一行为一个整数k表示将会打击的要塞数量。1<=k<=n。 接下来k行每行一个整数v表示会打击的城市这k个数互不相同,1<=v<=n。 输出
第一行输出最初的连通块个数,后k行一次输出每次打击后的结果。 样例输入
8 13 1 2 2 7 7 6 6 1 1 7 2 3 3 4 4 5 5 6 8 2 8 3 8 7 4 7 5 2 7 4 6 8 样例输出
1 1 1 2 3 3
要能够想到 是倒着来求解。 代码
#include<bits/stdc++.h> #define LL long long using namespace std; const int MAXN = 100000+10; const int MAXM = 200000; struct Edge { int from,to,nexts; }edge[MAXM<<2]; int head[MAXN],top; int pre[MAXN]; int n,m; void init(){ memset(head,-1,sizeof(head)); top=0; for(int i=0;i<=n;i++) pre[i]=i; } void addedge(int a,int b){ Edge e={a,b,head[a]}; edge[top]=e;head[a]=top++; } int Find(int x){ return x==pre[x]?x:pre[x]=Find(pre[x]); } void join(int a,int b){ a=Find(a);b=Find(b); if(a!=b) pre[a]=b; } int hit[MAXN]; map<int,bool>vis; map<int,int>M; int ans[MAXN]; void solve(){ for(int i=1;i<=m;i++){ int a,b; scanf("%d%d",&a,&b); addedge(a,b); addedge(b,a); } int k;cin>>k; for(int i=1;i<=k;i++) { scanf("%d",&hit[i]); vis[hit[i]] =1; } for(int now=1;now<=n;now++){ if(vis[now]) continue; for(int i=head[now];i!=-1;i=edge[i].nexts){ Edge e=edge[i]; if(vis[e.to]) continue; join(e.from,e.to); } } //puts("=========="); for(int i=1;i<=n;i++) { if(vis[i]) continue; M[Find(i)]++; } ans[k+1]=M.size(); // printf(" nas[k+1] %d\n",ans[k+1]); for(int now=k;now>=1;now--){ vis[hit[now]]=0; ans[now]=ans[now+1]+1;// 因为初始多一个点,肯定多一个联通快 for(int i=head[hit[now]];i!=-1;i=edge[i].nexts){ Edge e=edge[i]; if(vis[e.to]) continue; int t1=Find(e.from);int t2=Find(e.to); if(t1!=t2){ //合并一次,联通快肯定少一个 ans[now]--; pre[t1]=t2; } } } for(int i=1;i<=k+1;i++) printf("%d\n",ans[i]) ; } int main(){ scanf("%d%d",&n,&m); init(); solve(); return 0; }