[洛谷P4847]银河英雄传说V2
题目大意:有$n(n\leqslant2\times10^5)$个序列,有$m(m\leqslant2\times10^5)$个操作,分三种:
1. $M\;x\;y:$把$x$所在的序列放在$y$所在序列之后
2. $D\;x:$把$x$所在的序列从它前面断开
3. $Q\;x\;y:$询问若$x,y$在同一序列中,它们之间的元素和
题解:平衡树,合并就正常合并,注意是把$x$放到$y$后,关于找$x$所在的序列,就记录每个节点的父亲,直接向上跳父亲就可以了,在分裂时注意维护父亲。
求元素的排名就看一下它是不是它父亲的右儿子,是的话把它兄弟的大小加上。
询问就记录一个区间和即可。
卡点:无
C++ Code:
#include <algorithm> #include <cstdio> #include <cstdlib> #define maxn 200010 namespace Treap { int pri[maxn], lc[maxn], rc[maxn], fa[maxn], sz[maxn], V[maxn]; long long S[maxn]; int ta, tb, tmp, res; inline void nw(int pos, int x) { pri[pos] = rand(); S[pos] = V[pos] = x; lc[pos] = rc[pos] = fa[pos] = 0; sz[pos] = 1; } inline int update(int rt) { const int lc = Treap::lc[rt], rc = Treap::rc[rt]; if (lc) fa[lc] = rt; if (rc) fa[rc] = rt; sz[rt] = sz[lc] + sz[rc] + 1; S[rt] = S[lc] + S[rc] + V[rt]; return rt; } void split(int rt, int k, int &x, int &y) { if (!rt) x = y = 0; else { if (sz[lc[rt]] >= k) { split(lc[rt], k, x, lc[rt]); fa[x] = fa[rt] = 0; y = update(rt); } else { split(rc[rt], k - sz[lc[rt]] - 1, rc[rt], y); fa[rt] = fa[y] = 0; x = update(rt); } } } int merge(int x, int y) { if (!x || !y) return x | y; if (pri[x] < pri[y]) { rc[x] = merge(rc[x], y); return update(x); } else { lc[y] = merge(x, lc[y]); return update(y); } } inline int gtrnk(int x) { res = sz[lc[x]] + 1; while (x) { if (rc[fa[x]] == x) res += sz[lc[fa[x]]] + 1; x = fa[x]; } return res; } inline int gtrt(int x) { while (fa[x]) x = fa[x]; return x; } inline void Merge(int x, int y) { x = gtrt(x), y = gtrt(y); if (x == y) return ; merge(y, x); } inline void Split(int x) { int rk = gtrnk(x); x = gtrt(x); split(x, rk - 1, ta, tb); } inline void query(int x, int y) { int root = gtrt(x); if (root != gtrt(y)) { puts("-1"); return ; } int rkx = gtrnk(x), rky = gtrnk(y); if (rkx > rky) std::swap(rkx, rky); split(root, rky, ta, tb); split(ta, rkx - 1, ta, tmp); printf("%lld\n", S[tmp]); merge(ta, merge(tmp, tb)); } } int n, m; int main() { srand(20040826); scanf("%d%d", &n, &m); for (int i = 1, x; i <= n; ++i) { scanf("%d", &x); Treap::nw(i, x); } while (m --> 0) { char op; int x, y; scanf("%1s%d", &op, &x); switch (op) { case 'M': scanf("%d", &y); Treap::Merge(x, y); break; case 'D': Treap::Split(x); break; case 'Q': scanf("%d", &y); Treap::query(x, y); } } return 0; }