反向思考 || P1197 [JSOI2008] 星球大战
# [JSOI2008] 星球大战
## 题目描述
很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。
某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。
但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。
现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。
## 输入格式
输入文件第一行包含两个整数,$n,m$,分别表示星球的数目和以太隧道的数目。星球用 $0 \sim n-1$ 的整数编号。
接下来的 $m$ 行,每行包括两个整数 $x,y$,表示星球 $x$ 和星球 $y$ 之间有 “以太” 隧道,可以直接通讯。
接下来的一行为一个整数 $k$ ,表示将遭受攻击的星球的数目。
接下来的 $k$ 行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这 $k$ 个数互不相同,且都在 $0$ 到 $n-1$ 的范围内。
## 输出格式
第一行是开始时星球的连通块个数。接下来的 $k$ 行,每行一个整数,表示经过该次打击后现存星球的连通块个数。
## 样例 #1
### 样例输入 #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
3
```
## 提示
【数据范围】
对于 $100\%$ 的数据,$1\le m \le 2\times 10^5$,$1\le n \le 2m$,$x \neq y$。
[JSOI2008]
题解 :
从正向思考的话就先将所有点读入并用并查集维护起来,然后读入删除操作,每删除一个点更新一下并查集状态,时间复杂度大约是O(knlog * n)约等于n ^ 2,所以是不可取(况且我好像没做过并查集的删除操作)。
正难则反,本来想着会用更高级的数据结构维护其实不用。只需要反过来思考,先把所有点读入连边并且把删除的点先标记下来,接着对所有连边的点用并查集维护,然后一步步加入删除的点,根据删除点的出边来更新并查集状态即可。时间复杂度约为O(n * 出边数量 * log * n) (这个题貌似时间卡的不紧,目前我也不太清楚出边数量如何估计)。
代码:
1 #include <bits/stdc++.h> 2 #define gogo ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL); 3 using namespace std; 4 const string YES = "Yes"; 5 const string NO = "No"; 6 7 const int N = 4e5 + 10; 8 vector<int> edge[N]; 9 int p[N], ans[N], kk[N]; 10 bool st[N]; 11 int n, m, k; 12 13 int find(int x) { 14 if (p[x] != x) p[x] = find(p[x]); 15 return p[x]; 16 } 17 int32_t main() { 18 gogo; 19 cin >> n >> m; 20 for (int i = 0;i < n;i ++) 21 p[i] = i; 22 for (int i = 0;i < m;i ++) { 23 int x, y; 24 cin >> x >> y; 25 edge[x].push_back(y); 26 edge[y].push_back(x); 27 } 28 cin >> k; 29 for (int i = 0;i < k;i ++) { 30 cin >> kk[i]; 31 st[kk[i]] = true; 32 } 33 int cnt = n - k; 34 for (int i = 0;i < n;i ++) { 35 //cout << "ZZZ" << ' ' << cnt << endl; 36 if (st[i]) continue; 37 for (auto v : edge[i]) { 38 if (st[v]) continue; 39 int x = find(i), y = find(v); 40 if (x != y) { 41 p[x] = y; 42 cnt --; 43 } 44 } 45 } 46 vector<int> ans(k); 47 for (int i = k - 1;i >= 0;i --) { 48 ans[i] = cnt; 49 //cout << "ZZZ" << ' ' << i << ' ' << ans[i] << ' '; 50 cnt ++; 51 st[kk[i]] = false; 52 for (auto v : edge[kk[i]]) { 53 if (st[v]) continue; 54 int x = find(kk[i]), y = find(v); 55 if (x != y) { 56 p[x] = y; 57 cnt --; 58 } 59 } 60 } 61 cout << cnt << '\n'; 62 for (int i = 0;i < k;i ++) 63 cout << ans[i] << '\n'; 64 return 0; 65 }

浙公网安备 33010602011771号