星系探索
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可能会进行若干次实验,对于他的每一次实验,请你告诉他这一次实验能量收集器的最终能量是多少。
Input
第一行一个整数n,表示星系的星球数。
接下来n-1行每行一个整数,分别表示星球2-n的依赖星球编号。
接下来一行n个整数,表示每个星球在时刻0时的初始能量系数wi.
接下来一行一个整数m,表示事件的总数。
事件分为以下三种类型。
(1)”Q di”表示小C要开始一次实验,收集器的初始位置在星球di.
(2)”C xi yi”表示星球xi的依赖星球变为了星球yi.
(3)”F pi qi”表示星球pi能量激发,常数为qi.
Output
对于每一个事件类型为Q的事件,输出一行一个整数,表示此次实验的收集器最终能量。
Sample Input
3
1
1
4 5 7
5
Q 2
F 1 3
Q 2
C 2 3
Q 2
Sample Output
9
15
25
HINT
n<=100000,m<=300000,1< di,xi<=n,wi,qi<=100000.保证操作合法。注意w_i>=0
传说中的ETT……
以下代码部分内容均为本蒟蒻脑补,又丑又慢请见谅……
思路:
看到换父亲,嗯,动态树。
看到子树修改,LCT……?
不对啊LCT没法子树修改啊……
考虑一种叫做Euler Tour Tree的东西。
看名字识算法,这种树显然是在维护欧拉序。
只是,普通的欧拉序是不支持换父亲等操作的。
那么 考虑维护一个动态的欧拉序列:
上图序列为:
1 2 3 3 4 5 5 6 6 4 2 7 8 8 9 9 7 1
考虑把树切成这样:
欧拉序变成了这样:
1 2 3 3 2 7 8 4 5 5 6 6 4 8 9 9 7 1
可以发现,上图将4的父亲改为了8,对应了在欧拉序中将4两次出现的位置之间形成的序列接到其父亲8号节点的两次出现的位置之间。
那么因为在欧拉序里,每棵子树都是一个区间,现在我们就可以用这种切割欧拉序的方法维护子树了!
对于这种切割,当然是splay最方便。
考虑欧拉序上每个节点维护它的点权,但在第二次出现的位置取反,这样我们便可以把答案表示成欧拉序的前缀和,因为这样只有被查询节点到根节点路径上的点不会被两次经过,而被两次经过的点的贡献一正一负抵消了。
然后就是维护区间切割、区间加、前缀查询,都是基本操作辣~
提示:下面这份代码很慢,仅可用于对拍~
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define getchar getchar_unlocked
typedef long long ll;
const int N=100009;
int n,m;
int to[N<<1],nxt[N<<1],beg[N],tot;
int v[N],seg[N<<1],dfn=1;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || '9'<ch)ch=getchar();
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
inline void wrote(ll x)
{
if(x>=10)wrote(x/10);
putchar('0'+x%10);
}
inline void write(ll x)
{
wrote(x);
putchar('\n');
}
inline void add(int u,int v)
{
to[++tot]=v;
nxt[tot]=beg[u];
beg[u]=tot;
}
namespace splay
{
int root,stk[N],top;
int ch[N<<1][2],fa[N<<1];
ll sum[N<<1],val[N<<1],tag[N<<1],siz[N<<1];
inline void update(int x)
{
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+((x==1 || x==(n<<1|1)+1)?0:(x&1?-1:1));
}
inline void addv(int x,int v)
{
tag[x]+=v;
val[x]+=(x&1?-1:1)*v;
sum[x]+=siz[x]*v;
}
inline void push(int x)
{
if(tag[x])
{
if(ch[x][0])addv(ch[x][0],tag[x]);
if(ch[x][1])addv(ch[x][1],tag[x]);
tag[x]=0;
}
}
inline void prepush(int x)
{
top=0;
for(int i=x;fa[i];i=fa[i])
stk[++top]=i;
if(x!=root)stk[++top]=root;
while(top)push(stk[top--]);
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int l=(ch[y][1]==x);
if(z)ch[z][ch[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[ch[x][l^1]]=y;
ch[y][l]=ch[x][l^1];ch[x][l^1]=y;
update(y);update(x);
}
inline void splay(int x,int border=0)
{
prepush(x);
while(fa[x]!=border)
{
int y=fa[x],z=fa[y];
if(z!=border)
{
if((ch[z][0]==y)^(ch[y][0]==x))rotate(x);
else rotate(y);
}
rotate(x);
}
if(!border)
root=x;
update(x);
}
inline int nxt(int x)
{
splay(x);
int ret=ch[x][1];
while(ch[ret][0])
ret=ch[ret][0];
return ret;
}
inline int las(int x)
{
splay(x);
int ret=ch[x][0];
while(ch[ret][1])
ret=ch[ret][1];
return ret;
}
inline int build(int l,int r)
{
int mid=l+r>>1,now=seg[mid];
if(l<mid)
fa[ch[now][0]=build(l,mid-1)]=now;
if(mid<r)
fa[ch[now][1]=build(mid+1,r)]=now;
update(now);
return now;
}
inline ll query(int x)
{
splay(x<<1);
return sum[ch[x<<1][0]]+val[x<<1];
}
inline void modify(int x,ll v)
{
int lb=las(x<<1),rb=nxt(x<<1|1);
splay(lb);
splay(rb,lb);
addv(ch[rb][0],v);
}
inline void relink(int x,int y)
{
int cur,ls=las(x<<1),nx=nxt(x<<1|1);
splay(ls);
splay(nx,ls);
fa[cur=ch[nx][0]]=0;ch[nx][0]=0;
int z=nxt(y<<1);
splay(y<<1);
splay(z,y<<1);
ch[z][0]=cur;
fa[cur]=z;
update(z);
update(y<<1);
}
}
inline void dfs(int u,int fa)
{
seg[++dfn]=u<<1;splay::val[u<<1]=v[u];
for(int i=beg[u];i;i=nxt[i])
if(to[i]!=fa)
dfs(to[i],u);
seg[++dfn]=u<<1|1;splay::val[u<<1|1]=-v[u];
}
int main()
{
n=read();
for(int i=2;i<=n;i++)
add(read(),i);
for(int i=1;i<=n;i++)
v[i]=read();
dfs(1,1);
splay::root=splay::build(2,n<<1|1);
splay::ch[2][0]=1;splay::fa[1]=2;
splay::ch[3][1]=((n+1)<<1);
splay::fa[(n+1)<<1]=3;
m=read();
char s[5];
for(int i=1;i<=m;i++)
{
scanf("%s",s+1);
if(s[1]=='Q')
write(splay::query(read()));
else if(s[1]=='C')
{
int x=read();
splay::relink(x,read());
}
else
{
int x=read();
splay::modify(x,read());
}
}
return 0;
}
本文介绍了一种星系探索问题的解决方案,利用ETT(Euler Tour Tree)算法来处理星系中星球依赖关系的变化及能量系数的更新。通过动态维护欧拉序列并运用splay树,有效地实现了子树的修改与查询。
504

被折叠的 条评论
为什么被折叠?



