这是一题套用并查集(又称不相交集)的题
在套用过程中有一个小技巧,可以避免并查集不能删边的缺点(当然,在实际应用中实用性不大)。
题意是说有n个星球组成一个连接的网络,很明显把星球看成点而有通讯连线看成有边相连,接下来有m个操作,有查询操作,添加边的操作和销毁边的操作,同时每个星球有一个权值,即每个点有一个权值,对一个点的查询操作返回的是与它间接或直接相连的星球中权值最大者的序号。
首先,查询的操作很明显带有自反对称和传递性,易想到用并查集来解决,但是并查集只有添加边和查询两种操作(如对并查集不明可baidu之,很多网页都有讲,是比较基础的数据结构,实现过程也很简单,又称不相交集),故需要一些技巧,就是读入并记录所有的操作,从最后一条操作开始处理,一开始的图是给定的图删去后来需要销毁的边后的图,记录答案,当读到销毁操作时再把需销毁的边加上去,这样就可以回避销毁操作(倒着来事实上把销毁操作变成了加边的操作)。
在实现时,可用vector来存答案和操作,节省代码量。
附上我的代码
1#include <cstdio>
2#include <vector>
3#include <utility>
4#include <algorithm>
5
6using namespace std;
7
8template<int MAXN>
9struct DisjointSet {
10 int p[MAXN];
11 int s[MAXN];
12
13 void init(int n) {
14 for (int i = 0; i < n; i++) {
15 p[i] = i;
16 }
17 }
18
19 int root(int x) {
20 return p[x] == x ? x : (p[x] = root(p[x]));
21 }
22
23 bool is_friend(int i, int j) {
24 return root(i) == root(j);
25 }
26
27 void set_friend(int i, int j) {
28 i = root(i);
29 j = root(j);
30 if (i != j) {
31 if (s[i] > s[j] || (s[i] == s[j] && i < j)) {
32 p[j] = i;
33 } else {
34 p[i] = j;
35 }
36 }
37 }
38};
39
40DisjointSet<10086> dset;
41
42int main() {
43 bool blank = false;
44 char buf[1024];
45 int n, m, q, a, b;
46
47 while (scanf("%d", &n) != EOF) {
48 if (blank) {
49 puts("");
50 } else {
51 blank = true;
52 }
53 dset.init(n);
54 for (int i = 0; i < n; ++i) {
55 scanf("%d", &dset.s[i]);
56 }
57 vector<pair<int, int> > v, vv, w;
58 scanf("%d", &m);
59 for (int i = 0; i < m; ++i) {
60 scanf("%d%d", &a, &b);
61 if (a > b) {
62 swap(a, b);
63 }
64 v.push_back(make_pair(a, b));
65 }
66 scanf("%d ", &q);
67 for (int i = 0; i < q; ++i) {
68 fgets(buf, sizeof(buf), stdin);
69 b = -1;
70 sscanf(buf, "%*s%d%d", &a, &b);
71 if (a > b) {
72 swap(a, b);
73 }
74 w.push_back(make_pair(a, b));
75 if (a != -1) {
76 vv.push_back(make_pair(a, b));
77 }
78 }
79
80 sort(v.begin(), v.end());
81 sort(vv.begin(), vv.end());
82 v.erase(set_difference(v.begin(), v.end(), vv.begin(), vv.end(), v.begin()), v.end());
83 for (vector<pair<int, int> >::const_iterator it = v.begin(); it != v.end(); ++it) {
84 dset.set_friend(it->first, it->second);
85 }
86 vector<int> ans;
87 while (!w.empty()) {
88 if (w.back().first == -1) {
89 b = w.back().second;
90 a = dset.root(b);
91 ans.push_back(dset.s[a] > dset.s[b] ? a : -1);
92 } else {
93 dset.set_friend(w.back().first, w.back().second);
94 }
95 w.pop_back();
96 }
97 int i;
98 for (i=ans.size()-1; i>=0; i--)
99 printf("%d\n",ans[i]);
100 }
101
102
103 return 0;
104}
对并查集是用class来实现。。