P1197 [JSOI2008]星球大战 [删边求连通块个数]

 展开

题目描述

很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。

某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。

但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。

现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。

输入格式

输入文件第一行包含两个整数,n,mn,m,分别表示星球的数目和以太隧道的数目。星球用 0 sim n-10n1 的整数编号。

接下来的 mm 行,每行包括两个整数 x,yx,y,表示星球 xx 和星球 yy 之间有 “以太” 隧道,可以直接通讯。

接下来的一行为一个整数 kk ,表示将遭受攻击的星球的数目。

接下来的 kk 行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这 kk 个数互不相同,且都在 00 到 n-1n1 的范围内。

输出格式

第一行是开始时星球的连通块个数。接下来的 kk 行,每行一个整数,表示经过该次打击后现存星球的连通块个数。

输入输出样例

输入 #1
8 13
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
输出 #1
1
1
1
2
3

 

 思路

  对于删边求连通块个数问题, 就我遇到的来说,一般可以考虑暴搜,并查集和tarjan来做.

  对于此题来说, 显然用并查集做更简单, 考虑用并查集实现.

  由于并查集写删边的码量很大很复杂, 但是建边很容易, 所以考虑反向处理.

  首先, 令所有点都是单独的连通块, 因为炸掉了就不能参与构成连通块了, 所以先处理出剩下了多少可以操作的连通块.

  令最终态就是此时的连通块个数, 接着反向加点连边.

  每加上一个点, 连通块的个数肯定就要 + 1 , 然后遍历炸点之前它可以连的边, 如果连上一个连通块, 那么对应的此时连通块个数就要 - 1 ;

  最后就可以处理出每个点炸点前的连通块数量, 反向输出即可.

CODE

  1 #include <bits/stdc++.h>
  2 #define dbg(x) cout << #x << "=" << x << endl
  3 #define eps 1e-8
  4 #define pi acos(-1.0)
  5 
  6 using namespace std;
  7 typedef long long LL;
  8 
  9 template<class T>inline void read(T &res)
 10 {
 11     char c;T flag=1;
 12     while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
 13     while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
 14 }
 15 
 16 namespace _buff {
 17     const size_t BUFF = 1 << 19;
 18     char ibuf[BUFF], *ib = ibuf, *ie = ibuf;
 19     char getc() {
 20         if (ib == ie) {
 21             ib = ibuf;
 22             ie = ibuf + fread(ibuf, 1, BUFF, stdin);
 23         }
 24         return ib == ie ? -1 : *ib++;
 25     }
 26 }
 27 
 28 int qread() {
 29     using namespace _buff;
 30     int ret = 0;
 31     bool pos = true;
 32     char c = getc();
 33     for (; (c < '0' || c > '9') && c != '-'; c = getc()) {
 34         assert(~c);
 35     }
 36     if (c == '-') {
 37         pos = false;
 38         c = getc();
 39     }
 40     for (; c >= '0' && c <= '9'; c = getc()) {
 41         ret = (ret << 3) + (ret << 1) + (c ^ 48);
 42     }
 43     return pos ? ret : -ret;
 44 }
 45 
 46 const int maxn = 2e5 + 7;
 47 
 48 int cnt, head[maxn << 2], edge[maxn << 2], nxt[maxn << 2], from[maxn << 2];
 49 bool vis[maxn << 1];
 50 int a[maxn << 1], ans[maxn << 1],fa[maxn << 1];
 51 int tot,n,m,x,y,k;
 52 
 53 void BuildGraph(int u, int v) {
 54     cnt++;
 55     edge[cnt] = v;
 56     from[cnt] = u;
 57     nxt[cnt] = head[u];
 58     head[u] = cnt;
 59 }
 60 
 61 int fid(int x) {
 62     return x == fa[x] ? x : fa[x] = fid(fa[x]);
 63 }
 64 
 65 int main()
 66 {
 67     scanf("%d %d",&n, &m);
 68     for ( int i = 1; i <= m; ++i ) {
 69         int u, v;
 70         scanf("%d %d",&u, &v);
 71         BuildGraph(u, v);
 72         BuildGraph(v, u);
 73     }
 74     scanf("%d",&k);
 75     for ( int i = 1; i <= k; ++i ) {
 76         int x;
 77         scanf("%d",&x);
 78         vis[x] = 1;
 79         a[i] = x;
 80     }
 81     for ( int i = 1; i <= n; ++i ) {
 82         fa[i] = i;
 83     }
 84     tot = n - k;
 85     for ( int i = 1; i <= 2 * m; ++i ) {
 86         if(!vis[from[i]] && !vis[edge[i]]) {
 87             if(fa[fid(from[i])] != fa[fid(edge[i])]) {
 88                 tot--;
 89                 fa[fid(from[i])] = fa[fid(edge[i])];
 90             }
 91         }
 92     }
 93     ans[k+1] = tot;
 94     for ( int i = k; i >= 1; --i ) {
 95         int x = a[i];
 96         vis[x] = 0;
 97         tot++;
 98         for ( int j = head[x]; j; j = nxt[j] ) {
 99             if(vis[edge[j]] == 0 && fa[fid(x)] != fa[fid(edge[j])]) {
100                 tot--;
101                 fa[fid(x)] = fa[fid(edge[j])];
102             }
103         }
104         ans[i] = tot;
105     }
106     for ( int i = 1; i <= k+1; ++i ) {
107         printf("%d
",ans[i]);
108     }
109     return 0;
110 }
View Code
原文地址:https://www.cnblogs.com/orangeko/p/12375443.html