染色祖先:距离某节点最近的染色节点称为祖先,求染色过程中各节点祖先。
2.4 加工并储存数据的数据结构
并查集
稍有些复杂,需要bfs和两棵树。一棵树用来维护并查集,一棵树用来保存原来的树。bfs用来求解每个节点的祖先。也许看到这里你很好奇:既然节点的祖先是用bfs求出来的,那并查集是干什么吃的?这题的复杂之处在于,它是多用例,如果每染色一个节点就跑一遍bfs,那么会TLE。并查集用来省时间的。
思路是先染色,同时用个栈保留这些操作。接着跑一遍bfs,得到祖先的最终状态,并且将祖先相同的节点放入同一个集合中。然后从栈里面弹出操作将节点染回来,“反染色”的过程中,更新并查集和祖先。
注意两点:
-
染色节点的祖先是自己
-
可能存在重复染色,忽略它们
#include <iostream> #include <vector> #include <queue> #include <stack> using namespace std; // 并查集相关数据与算法 #define MAX_N 100000 + 16 int parent[MAX_N]; int height[MAX_N]; void init(const int& n) { for (int i = 0; i < n; ++i) { parent[i] = i; height[i] = 0; } } int find(const int& x) { if (parent[x] == x) { return x; } else { return parent[x] = find(parent[x]); } } void unite(int x, int y) { x = find(x); y = find(y); if (x == y) { return; } if (height[x] < height[y]) { parent[x] = y; } else { parent[y] = x; if (height[x] == height[y]) { ++height[x]; } } } bool same(const int& x, const int& y) { return find(x) == find(y); } // End Of 并查集 // 原始的树用它来描述 vector<int> children[MAX_N]; int parent_tree[MAX_N]; bool marked[MAX_N]; int ancestor[MAX_N]; // 每条指令被拆分为 操作 + 目标 stack<char> operation; stack<int> target; void bfs(int index, int the_ancestor) { queue<int> q_index; queue<int> q_ancestor; q_index.push(index); q_ancestor.push(the_ancestor); while (!q_index.empty()) { the_ancestor = q_ancestor.front(); q_ancestor.pop(); index = q_index.front(); q_index.pop(); if (marked[index] == true) { the_ancestor = index; } ancestor[index] = the_ancestor; for (vector<int>::iterator it = children[index].begin(); it != children[index].end(); ++it) { q_index.push(*it); q_ancestor.push(the_ancestor); } } } ///////////////////////////SubMain////////////////////////////////// int main(int argc, char *argv[]) { int N, Q; while (cin >> N, cin >> Q, N) { for (int i = 0; i < N; ++i) { children[i].clear(); marked[i] = false; } marked[0] = true; int p; for (int i = 1; i < N; ++i) { cin >> p; --p; parent_tree[i] = p; children[p].push_back(i); } for (int i = 0; i < Q; ++i) { char o; int t; cin >> o >> t; --t; if (o == 'M') { if (marked[t]) { continue; } else { marked[t] = true; } } operation.push(o); target.push(t); } bfs(0, 0); init(N); for (int i = 0; i < N; ++i) { unite(i, ancestor[i]); } unsigned long long result = 0; while (!operation.empty()) { char o = operation.top(); operation.pop(); int t = target.top(); target.pop(); if (o == 'Q') { result += ancestor[find(t)] + 1; // 题目index从1开始 } else { // 执行“反染色”操作,之后这个节点的祖先变为其父节点的祖先 int p = ancestor[find(parent_tree[t])]; unite(t, parent_tree[t]); ancestor[find(t)] = p; } } cout << result << endl; } return 0; } ///////////////////////////End Sub//////////////////////////////////
速度还行,从上表看出,AOJ竟然支持 C++11 了!而且与 C++ 视为两种不同的语言!看来第五版的《C++ Primary》里说C++11是一种全新的语言真是诚不我欺也!
知识共享署名-非商业性使用-相同方式共享:码农场 » AOJ 2170: Marked Ancestor 题解 《挑战程序设计竞赛》
求您认真去计算一下复杂度,别在这里无知。最坏情况1e10
请问做题用的是什么工具啊?