Bzoj3786: 星系探索——Splay
题面
解析
上课讲稿上的例题
这道题是套路题,是括号序的应用,进入节点时打上$+1$标记, 退出时打上$-1$标记,这个是作为点权的系数
先看操作2, 需要更改父节点,就是把一段区间提取出来,插入另一个地方,显然可以用Splay维护,先提取区间,再把新父亲的$+1$点旋转至根,把区间挂在根的后继的左儿子上,再把这个节点旋转至根,以更新信息
对于操作1,求点到根的路径和,就是求括号序列的前缀和,该点对应的$+1$点或$-1$点的前缀和都可,我是把$-1$的点旋转至根,答案就是根的左儿子的子树和,因此需要维护子树和
最后还有操作3,显然打标记,问题在对于子树和的改变, 记该子树中$+1$标记有$cnt1$个,$-1$标记有$cnt_1$个,则子树和需要加$(cnt1 - cnt_1) * delta$,因此需要维护子树中$+1$与$-1$的个数
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int maxn = 100004; template<class T> void read(T &re) { re=0; T sign=1; char tmp; while((tmp=getchar())&&(tmp<'0'||tmp>'9')) if(tmp=='-') sign=-1; re=tmp-'0'; while((tmp=getchar())&&(tmp>='0'&&tmp<='9')) re=(re<<3)+(re<<1)+(tmp-'0'); re*=sign; } int n, m, root, cnt, a[maxn], fir[maxn], sec[maxn]; vector<int> G[maxn]; struct spaly_tree{ int s[2], fa, val, add, cnt1, cnt_1, fl[2]; ll sum; }tr[maxn<<1]; void dfs(int x) { fir[x] = ++cnt; tr[cnt].fl[1] = 1; tr[cnt].val = a[x]; tr[cnt].sum = (ll)a[x]; for(unsigned int i = 0; i < G[x].size(); ++i) { int id = G[x][i]; dfs(id); } sec[x] = ++cnt; tr[cnt].fl[0] = 1; tr[cnt].val = -a[x]; tr[cnt].sum = (ll)(-a[x]); } void build(int l, int r, int ff) { int mid = (l + r)>>1; if(l < mid) build(l, mid - 1, mid); if(mid < r) build(mid + 1, r, mid); if(ff) tr[ff].s[ff < mid] = mid; int ls = tr[mid].s[0], rs = tr[mid].s[1]; tr[mid].fa = ff; tr[mid].cnt1 = tr[ls].cnt1 + tr[rs].cnt1 + tr[mid].fl[1]; tr[mid].cnt_1 = tr[ls].cnt_1 + tr[rs].cnt_1 + tr[mid].fl[0]; tr[mid].sum += tr[ls].sum + tr[rs].sum; } void spread(int x) { if(tr[x].add) { int ls = tr[x].s[0], rs = tr[x].s[1]; if(ls) { tr[ls].sum += 1LL * (tr[ls].cnt1 - tr[ls].cnt_1) * tr[x].add; tr[ls].val += (tr[ls].fl[1]? tr[x].add: -tr[x].add); tr[ls].add += tr[x].add; } if(rs) { tr[rs].sum += 1LL * (tr[rs].cnt1 - tr[rs].cnt_1) * tr[x].add; tr[rs].val += (tr[rs].fl[1]? tr[x].add: -tr[x].add); tr[rs].add += tr[x].add; } tr[x].add = 0; } } void update(int x) { int ls = tr[x].s[0], rs = tr[x].s[1]; tr[x].sum = tr[ls].sum + tr[rs].sum + 1LL * tr[x].val; tr[x].cnt1 = tr[ls].cnt1 + tr[rs].cnt1 + tr[x].fl[1]; tr[x].cnt_1 = tr[ls].cnt_1 + tr[rs].cnt_1 + tr[x].fl[0]; } void Rotate(int x) { int y = tr[x].fa, z = tr[y].fa, k = (tr[y].s[1] == x), w = (tr[z].s[1] == y), son = tr[x].s[k^1]; spread(y);spread(x); tr[y].s[k] = son;tr[son].fa = y; tr[x].s[k^1] = y;tr[y].fa = x; tr[z].s[w] = x;tr[x].fa = z; update(y);update(x); } void Splay(int x, int to) { int y, z; while(tr[x].fa != to) { y = tr[x].fa; z = tr[y].fa; if(z != to) Rotate((tr[y].s[0] == x) ^ (tr[z].s[0] == y)? x: y); Rotate(x); } if(!to) root = x; } int Findpre() { spread(root); int now = tr[root].s[0]; while(tr[now].s[1]) { spread(now); now = tr[now].s[1]; } spread(now); return now; } int Findnxt() { spread(root); int now = tr[root].s[1]; while(tr[now].s[0]) { spread(now); now = tr[now].s[0]; } spread(now); return now; } void Remove(int x, int y) { Splay(fir[x], 0); int pre = Findpre(); Splay(sec[x], 0); int nxt = Findnxt(); Splay(pre, 0); Splay(nxt, pre); int now = tr[nxt].s[0]; tr[nxt].s[0] = 0; update(nxt);update(pre); Splay(fir[y], 0); nxt = Findnxt(); tr[now].fa = nxt; tr[nxt].s[0] = now; Splay(now, 0); } int main() { read(n); for(int i = 1; i < n; ++i) { int x; read(x); G[x].push_back(i+1); } for(int i = 1; i <= n; ++i) read(a[i]); cnt = 1; dfs(1); cnt ++; build(1, cnt, 0); tr[0].s[1] = root = (1 + cnt)>>1; read(m); for(int i = 1; i <= m; ++i) { char opt[3]; scanf("%s", opt); if(opt[0] == 'Q') { int x; read(x); Splay(sec[x], 0); printf("%lld\n", tr[tr[root].s[0]].sum); } else if(opt[0] == 'C') { int x, y; read(x);read(y); Remove(x, y); } else { int x, y; read(x);read(y); if(!y) continue; Splay(fir[x], 0); int pre = Findpre(); Splay(sec[x], 0); int nxt = Findnxt(); Splay(pre, 0); Splay(nxt, pre); int p = tr[nxt].s[0]; tr[p].sum += 1LL * (tr[p].cnt1 - tr[p].cnt_1) * y; tr[p].val += (tr[p].fl[1]? y: -y); tr[p].add += y; update(nxt);update(pre); } } return 0; }