题目大意:给出一棵树,要求有以下这些操作:1.求出一个节点到根的点权和。2.将一个节点的父亲改变。3.将一个子树中的每一个节点都加上一个权值。
思路:LCT就不用想了,因为有子树操作。然后就是一个很神奇的东西了,就是Splay维护树的入栈出栈序。这个玩应是做了这个题之后才知道的。但是感觉真的很dio。
首先,我们先按照题意,将树建出来。然后从根开始深搜,这样一个点进入DFS函数和出DFS函数的时候就会有两个时间点,就是入栈的时间和出栈的时间。然后利用Splay维护一个序列,就是入栈出栈的顺序。在数组中入栈存正,出栈存负。这个序列有很优美的性质,每一个节点的子树就是这个点的入栈和出栈之间加的东西。两点之间的路径就是一个点的入栈到另一个点的入栈。
然后这个题就是代码题了。。
CODE:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 600010 #define WORKPATH (root->son[1]->son[0]) using namespace std; struct SplayTree{ long long val,sum,c,size; long long plus,num,_num; SplayTree *son[2],*father; SplayTree(int _,int __); SplayTree() {} bool Check() { return father->son[1] == this; } void Combine(SplayTree *a,bool dir) { son[dir] = a; a->father = this; } void Plus(int c); void PushUp() { sum = son[0]->sum + son[1]->sum + val * (plus ? 1:-1); size = son[0]->size + son[1]->size + 1; num = son[0]->num + son[1]->num + (plus == 1); _num = son[0]->_num + son[1]->_num + (plus == 0); } void PushDown() { if(c) { son[0]->Plus(c); son[1]->Plus(c); c = 0; } } }none,*nil = &none,*root,*tree[MAX]; SplayTree:: SplayTree(int _,int __) { plus = __; if(__ == 1) ++num; if(__ == 0) ++_num; val = _; sum = _ * (plus ? 1:-1); size = 1; c = 0; son[0] = son[1] = nil; } void SplayTree:: Plus(int _) { if(this == nil) return ; sum += _ * (num - _num); val += _; c += _; } int points,asks; int head[MAX],total; int next[MAX],aim[MAX]; int src[MAX],pos[MAX]; int from[MAX],cnt; int p[MAX]; char c[10]; inline void Add(int x,int y) { next[++total] = head[x]; aim[total] = y; head[x] = total; } void DFS(int x) { pos[++cnt] = src[x]; from[cnt] = x; p[cnt] = true; for(int i = head[x]; i; i = next[i]) DFS(aim[i]); pos[++cnt] = src[x]; from[cnt] = x; p[cnt] = false; } void Pretreatment() { nil->son[0] = nil->son[1] = nil->father = nil; nil->val = nil->sum = nil->c = nil->size = 0; p[0] = p[cnt + 1] = -1; } SplayTree *BuildTree(int l,int r) { if(l > r) return nil; int mid = (l + r) >> 1; SplayTree *re = new SplayTree(pos[mid],p[mid]); if(p[mid]) tree[from[mid] << 1] = re; else tree[from[mid] << 1|1] = re; re->Combine(BuildTree(l,mid - 1),false); re->Combine(BuildTree(mid + 1,r),true); re->PushUp(); return re; } inline void Rotate(SplayTree *a,bool dir) { SplayTree *f = a->father; f->PushDown(),a->PushDown(); f->son[!dir] = a->son[dir]; f->son[!dir]->father = f; a->son[dir] = f; f->father->son[f->Check()] = a; a->father = f->father; f->father = a; f->PushUp(); if(root == f) root = a; } inline void Splay(SplayTree *a,SplayTree *aim) { while(a->father != aim) { if(a->father->father == aim) Rotate(a,!a->Check()); else if(!a->father->Check()) { if(!a->Check()) Rotate(a->father,true),Rotate(a,true); else Rotate(a,false),Rotate(a,true); } else { if(a->Check()) Rotate(a->father,false),Rotate(a,false); else Rotate(a,true),Rotate(a,false); } } a->PushUp(); } SplayTree *Find(SplayTree *a,int k) { a->PushDown(); if(a->son[0]->size >= k) return Find(a->son[0],k); k -= a->son[0]->size; if(k == 1) return a; return Find(a->son[1],k - 1); } inline void SplaySeg(SplayTree *a,SplayTree *b) { int size_1,size_2; Splay(a,nil); size_1 = root->son[0]->size + 1; Splay(b,nil); size_2 = root->son[0]->size + 1; Splay(Find(root,size_1 - 1),nil); Splay(Find(root,size_2 + 1),root); } int main() { cin >> points; for(int x,i = 2; i <= points; ++i) { scanf("%d",&x); Add(x,i); } for(int i = 1; i <= points; ++i) scanf("%d",&src[i]); DFS(1); Pretreatment(); root = BuildTree(0,cnt + 1); root->father = nil; cin >> asks; for(int x,y,i = 1; i <= asks; ++i) { scanf("%s",c); if(c[0] == 'Q') { scanf("%d",&x); SplaySeg(tree[1 << 1],tree[x << 1]); printf("%lld\n",WORKPATH->sum); } else if(c[0] == 'C') { scanf("%d%d",&x,&y); SplaySeg(tree[x << 1],tree[x << 1|1]); SplayTree *temp = WORKPATH; WORKPATH = nil; root->son[1]->PushUp(); root->PushUp(); Splay(tree[y << 1],nil); int k = root->son[0]->size + 1; Splay(Find(root,k + 1),root); root->son[1]->Combine(temp,false); root->son[1]->PushUp(); root->PushUp(); } else { scanf("%d%d",&x,&y); SplaySeg(tree[x << 1],tree[x << 1|1]); WORKPATH->Plus(y); } } return 0; }