题解 P3835 【【模板】可持久化平衡树】

GGN_2015

2018-09-29 19:54:11

Solution

”一道可持久化数据结构的题,如果不强制在线,那么它八成不是一道真正的可持久化数据结构的题。“ —— GGN_2015 惊奇地发现这道题其实并不需要任何可持久化数据结构。 我们只需要建立一颗”时光树“(我们暂且这样称呼它)。如果i时刻的平衡树是由$v_i$时刻发展而来的,那么我们就令”时光树“上i号结点的父亲为$v_i$。0号结点显然是整棵”时光树“的根结点。 维护一棵平衡树(我写的是treap),最开始的时候这是一棵空树。在”时光树“上DFS,每进入结点i时就在平衡树中进行操作i(修改数据结构或储存查询的结果),然后再依次DFS结点i的每个儿子。退出结点i时,就”撤销“这个结点对数据结构的修改。例如当$opt_i = 1$时,我们向数据结构中插入了元素$x_i$,离开结点i时,我们再把$x_i$从数据结构中删除。 比较特殊的是:当$opt_i = 2$时,删除是可能失败的。如果删除失败的话,我们在回溯的时候要特判(就是说不要把本就没被成功删除的$x_i$插入回数据结构中)。 附上代码之前先作出一个声明:我的treap的代码不支持可重集合,所以我用了一个类似pair的数据结构来储存元素(第一维存储元素的值,第二维记录操作的编号),这样可以保证treap中没有重复的元素。另外,我懒得写前去和后继的查询,所以我的”前驱“和”后继“查询是利用”排名“和”第k大“查询间接实现的。(我觉得这样写treap更好调试一些) ```cpp #include <cstdio> #include <cstdlib> #include <queue> #include <vector> #include <algorithm> using namespace std; struct item {int val, id;}; bool operator < (item A, item B) { if(A.val != B.val) return A.val < B.val; return A.id < B.id; } bool operator == (item A, item B) { return A.val==B.val && A.id==B.id; } const int maxn = (500000 + 6)*2; namespace treap { int ch[maxn][2], rnd[maxn], siz[maxn]; item key[maxn]; int ncnt; queue<int> Q; void maintain(int x) { siz[x] = 1 + siz[ch[x][0]] + siz[ch[x][1]]; } int newnode() { if(!Q.empty()) { int x = Q.front(); Q.pop(); ch[x][0] = ch[x][1] = rnd[x] = siz[x] = 0; key[x] = (item){0, 0}; return x; } return ++ ncnt; } void rotate(int& x, int d) { int k = ch[x][d^1]; ch[x][d^1] = ch[k][d]; ch[k][d] = x; maintain(x); x = k; maintain(k); } void insert(int& x, item v) { if(x == 0) { x = newnode(); rnd[x] = rand(); siz[x] = 1; key[x] = v; return; } int d = v < key[x] ? 0 : 1; insert(ch[x][d], v); maintain(x); if(rnd[x] > rnd[ch[x][d]]) rotate(x, d^1); } int rnk(int x, item v) { if(x == 0) return 1; int lsiz = 1 + siz[ch[x][0]]; if(v == key[x]) return lsiz; if(v < key[x]) return rnk(ch[x][0], v); else return rnk(ch[x][1], v) + lsiz; } item kth(int x, int k) { if(x==0 || k<=0 || k>siz[x]) return k<=0 ? (item){-2147483647, 0} : (item){2147483647, 0}; int lsiz = 1 + siz[ch[x][0]]; if(lsiz == k) return key[x]; if(k < lsiz) return kth(ch[x][0], k); else return kth(ch[x][1], k-lsiz); } int LSIZ(int x) {return 1 + siz[ch[x][0]];} void erase(int& x, int k) { if(x == 0) return; /// 删除元素时一定要特判检测是否相等 int lsiz = 1 + siz[ch[x][0]]; if(lsiz == k) { if(ch[x][0]==0 || ch[x][1]==0) { int tmp = x; x = ch[x][0] + ch[x][1]; Q.push(tmp); ch[tmp][0] = ch[tmp][1] = siz[tmp] = rnd[tmp] = 0; key[tmp] = (item){0, 0}; }else { if(rnd[ch[x][0]] > rnd[ch[x][1]]) { rotate(x, 0); erase(ch[x][0], LSIZ(ch[x][0])); }else { rotate(x, 1); erase(ch[x][1], LSIZ(ch[x][1])); } maintain(x); } return; }else { if(k < lsiz) erase(ch[x][0], k); else erase(ch[x][1], k-lsiz); maintain(x); } } } int root = 0, V[maxn], OPT[maxn], X[maxn], ANS[maxn]; namespace tree { vector<int> nxt[maxn]; void addedge(int f, int t) {nxt[f].push_back(t);} void dfs(int x) { bool delsuc = 0; /// 记录删除操作是否成功 if(OPT[x]) { /// 有操作 if(OPT[x] == 1) { /// 插入一个元素 treap::insert(root, (item){X[x], x}); }else if(OPT[x] == 2) { /// 删除一个元素 int rnk = treap::rnk(root, (item){X[x], 0}); /// 查询这个元素的排名 int get = treap::kth(root, rnk).val; /// 得到这个元素(可能为+-inf) if(get == X[x]) { /// 可以删除 treap::erase(root, rnk); delsuc = 1; } }else if(OPT[x] == 3) { /// 查排名 ANS[x] = treap::rnk(root, (item){X[x], 0}); }else if(OPT[x] == 4) { /// 查第k大 ANS[x] = treap::kth(root, X[x]).val; }else if(OPT[x] == 5) { /// prev int rnk = treap::rnk(root, (item){X[x], 0}) - 1; int get = treap::kth(root, rnk).val; ANS[x] = get; }else if(OPT[x] == 6) { /// next int rnk = treap::rnk(root, (item){X[x], 0x7f7f7f7f}); int get = treap::kth(root, rnk).val; ANS[x] = get; } } for(int i = 0; i < (int)nxt[x].size(); i ++) { int t = nxt[x][i]; dfs(t); /// 递归计算 } if(OPT[x] == 1) { /// 回滚插入操作 int rnk = treap::rnk(root, (item){X[x], 0}); /// 一定有 treap::erase(root, rnk); } if(OPT[x]==2 && delsuc) { treap::insert(root, (item){X[x], x}); } } } int main() { //freopen("nontime.in", "r", stdin); int n; scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("%d%d%d", &V[i], &OPT[i], &X[i]); tree::addedge(V[i], i); } tree::dfs(0); for(int i = 1; i <= n; i ++) { if(OPT[i]>=3) { printf("%d\n", ANS[i]); } } return 0; } ```