Description
物理学家小C的研究正遇到某个瓶颈。
他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球。主星球没有依赖星球。
我们定义依赖关系如下:若星球a的依赖星球是b,则有星球a依赖星球b.此外,依赖关系具有传递性,即若星球a依赖星球b,星球b依赖星球c,则有星球a依赖星球c.
对于这个神秘的星系中,小C初步探究了它的性质,发现星球之间的依赖关系是无环的。并且从星球a出发只能直接到达它的依赖星球b.
每个星球i都有一个能量系数wi.小C想进行若干次实验,第i次实验,他将从飞船上向星球di发射一个初始能量为0的能量收集器,能量收集器会从星球di开始前往主星球,并收集沿途每个星球的部分能量,收集能量的多少等于这个星球的能量系数。
但是星系的构成并不是一成不变的,某些时刻,星系可能由于某些复杂的原因发生变化。
有些时刻,某个星球能量激发,将使得所有依赖于它的星球以及他自己的能量系数均增加一个定值。还有可能在某些时刻,某个星球的依赖星球会发生变化,但变化后依然满足依赖关系是无环的。
现在小C已经测定了时刻0时每个星球的能量系数,以及每个星球(除了主星球之外)的依赖星球。接下来的m个时刻,每个时刻都会发生一些事件。其中小C可能会进行若干次实验,对于他的每一次实验,请你告诉他这一次实验能量收集器的最终能量是多少。
题解:
挺不错的一个套路题。如果没有换父亲的操作,那么树链剖分就能做了,有的话我们就要用一个东西——splay维护入栈出栈序。其实这个东西和dfs序是有一点类似的,就是在一个点进入dfs的时候(入栈)在当前位置加入一个正的权值,在结束dfs(出栈)时在当前位置加入一个负的权值,那么求一个点x到根的权值和,就可以求1到x入栈位置的权值和,因为此时只有x到根路径上的点在栈中。对于换父亲操作,只需要splay区间移动操作就可以了。注意把一段区间提取出来时不能简单的把l-1和r+1之间的提取,而需要找到排名在其前或后一位的位置。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int Maxn=200010;
#define LL long long
struct Node
{
int fa,son[2],d,type,size;
LL tag,sv,v,st;
}tr[Maxn<<1];//sv子树和 v节点权值 type节点类型 st子树节点类型和
int n;LL w[Maxn];
int tot=0,root;
void work(int x,LL v)
{
tr[x].v+=tr[x].type*v;
tr[x].sv+=tr[x].st*v;
tr[x].tag+=v;
}
void up(int x)
{
int lc=tr[x].son[0],rc=tr[x].son[1];
tr[x].sv=tr[x].v;tr[x].st=tr[x].type;tr[x].size=1;
if(lc)tr[x].sv+=tr[lc].sv,tr[x].st+=tr[lc].st,tr[x].size+=tr[lc].size;
if(rc)tr[x].sv+=tr[rc].sv,tr[x].st+=tr[rc].st,tr[x].size+=tr[rc].size;
}
void down(int x)
{
if(tr[x].tag)
{
LL t=tr[x].tag;
int lc=tr[x].son[0],rc=tr[x].son[1];
if(lc)work(lc,t);
if(rc)work(rc,t);
tr[x].tag=0;
}
}
void rotate(int x)
{
int y=tr[x].fa,z=tr[y].fa,w=(tr[y].son[0]==x);
tr[y].son[w^1]=tr[x].son[w];if(tr[x].son[w])tr[tr[x].son[w]].fa=y;
tr[z].son[tr[z].son[1]==y]=x;tr[x].fa=z;
tr[x].son[w]=y;tr[y].fa=x;
up(y),up(x);
}
int sta[Maxn],top;
void update(int x)
{
top=0;
while(x)sta[++top]=x,x=tr[x].fa;
while(top)down(sta[top--]);
}
void splay(int x,int rt)
{
update(x);
while(tr[x].fa!=rt)
{
int y=tr[x].fa,z=tr[y].fa;
if(z==rt)rotate(x);
else rotate(((tr[z].son[1]==y)==(tr[y].son[1]==x))?y:x),rotate(x);
}
if(!rt)root=x;
}
int find1(int rank)
{
int x=root;
while(1)
{
int lc=tr[x].son[0],rc=tr[x].son[1];
int ls=((lc)?tr[lc].size:0);
if(ls+1==rank)return x;
else if(rank<=ls)x=lc;
else rank-=(ls+1),x=rc;
}
}
int find2(int x)
{
if(x==root)return tr[tr[x].son[0]].size+1;
if(tr[tr[x].fa].son[1]==x)return find2(tr[x].fa)+tr[tr[x].son[0]].size+1;
return find2(tr[x].fa)-tr[tr[x].son[1]].size-1;
}
struct Edge{int y,next;}e[Maxn];
int len=0,last[Maxn];
void ins(int x,int y){int t=++len;e[t].y=y;e[t].next=last[x];last[x]=t;}
int dfn=1,L[Maxn],R[Maxn];
void dfs(int x)
{
L[x]=++dfn;tr[dfn].v=w[x],tr[dfn].type=1,tr[dfn].d=dfn,tr[dfn].tag=0;
for(int i=last[x];i;i=e[i].next)dfs(e[i].y);
R[x]=++dfn;tr[dfn].v=-w[x],tr[dfn].type=-1,tr[dfn].d=dfn,tr[dfn].tag=0;
}
void build(int l,int r,int Fa)
{
if(l>r)return;
int mid=l+r>>1;
tr[mid].fa=Fa;
if(Fa)tr[Fa].son[tr[mid].d>tr[Fa].d]=mid;
build(l,mid-1,mid);build(mid+1,r,mid);
up(mid);
}
void workQ()
{
int x;
scanf("%d",&x);
splay(L[x],0);
printf("%lld\n",tr[L[x]].v+tr[tr[L[x]].son[0]].sv);
}
void workF()
{
int p;LL q;
scanf("%d%lld",&p,&q);
int t1=find1(find2(L[p])-1),t2=find1(find2(R[p])+1);
splay(t1,0);splay(t2,t1);
work(tr[t2].son[0],q);
}
void workC()
{
int x,y;
scanf("%d%d",&x,&y);
int t1=find1(find2(L[x])-1),t2=find1(find2(R[x])+1);
splay(t1,0);splay(t2,t1);
down(t1);down(t2);
int tmp=tr[t2].son[0];
tr[tmp].fa=0;
tr[t2].son[0]=0;
up(t2);up(t1);
t1=find1(find2(R[y])-1);
splay(t1,0);splay(R[y],t1);
down(t1);down(R[y]);
tr[tmp].fa=R[y];
tr[R[y]].son[0]=tmp;
up(R[y]);up(t1);
}
int main()
{
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
int father;
scanf("%d",&father);
ins(father,i);
}
for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
dfs(1);
tr[1].d=tr[1].sv=tr[1].st=0;
tr[dfn+1].d=dfn+1;tr[dfn+1].sv=tr[dfn+1].st=0;
root=(dfn+1)>>1;
build(1,dfn+1,0);
int Q;
scanf("%d",&Q);
while(Q--)
{
char op[3];
scanf("%s",op);
if(op[0]=='Q')workQ();
else if(op[0]=='F')workF();
else workC();
}
}