题目大意:给出一棵树,要求有以下这些操作: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;
}