[BZOJ3786]星系探索

时间:2023-03-09 00:43:09
[BZOJ3786]星系探索

[BZOJ3786]星系探索

试题描述

物理学家小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可能会进行若干次实验,对于他的每一次实验,请你告诉他这一次实验能量收集器的最终能量是多少。

输入

第一行一个整数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.

输出

对于每一个事件类型为Q的事件,输出一行一个整数,表示此次实验的收集器最终能量。

输入示例


Q
F
Q
C
Q

输出示例


数据规模及约定

n<=100000,m<=300000,1<di,xi<=n,wi,qi<=100000.保证操作合法。

题解

这题的妙点在于 dfs 序的优美,注意这题要的是把每个点拆成入栈时刻和出栈时刻两个点,设节点 i 的入栈时刻为 dli,出栈时刻为 dri,点权为 val[i],那么设 v[dli] = val[i],v[dri] = -val[i],同时记一个标记 s,s[dli] = 1, s[dri] = -1。现在考虑不带换父亲操作,点 i 到根节点的距离等于 Σv[j] (1 ≤ j ≤ dli);修改某子树权值可以视为序列上的区间修改,假设要修改子树 i,则将区间 [dli, dri] 打上懒标记 addv,下传标记的时候该区间维护的权值和加上 Σs[j] (dli ≤ j ≤ dri) * addv 即可,正确性留给读者证(y)明(y)。

上面的东西可以用线段树维护。至于加上了换父亲(C)操作,就可以用 splay 来搞,C操作显然是将一段区间移位,那么可以通过一系列伸展树的分裂、合并操作完成(详细地说就是将指定区间分离出来,再插入到目标位置里,再详细一点请参见代码)。F操作可以将指定区间分离出来,打一个懒标记,再合并成原序列。splay 的标记下传规则跟线段树的一样。

能写出代码,这题就能 AC 了= =。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std; const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
int read() {
int x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
} #define maxn 100010
#define maxm 200010
#define LL long long
int n; int m, head[maxn], next[maxm], to[maxm];
int clo, val[maxn], dl[maxn], dr[maxn], ids[maxn<<1], dv[maxn<<1], ds[maxn<<1];
void AddEdge(int a, int b) {
to[++m] = b; next[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; next[m] = head[a]; head[a] = m;
return ;
}
void build(int u, int fa) {
dl[u] = ++clo; dv[clo] = val[u]; ds[clo] = 1;
for(int e = head[u]; e; e = next[e]) if(to[e] != fa) build(to[e], u);
dr[u] = ++clo; dv[clo] = -val[u]; ds[clo] = -1;
return ;
} struct Node {
int sym, sums;
LL val, sumv, addv;
Node() { val = sumv = addv = sym = sums = 0; }
void clear() { val = sumv = addv = sym = sums = 0; return ; }
} nodes[maxn<<1];
int ToT, root, ch[maxn<<1][2], fa[maxn<<1];
void maintain(int u) {
nodes[0].clear();
Node &o = nodes[u], &l = nodes[ch[u][0]], &r = nodes[ch[u][1]];
o.sums = l.sums + o.sym + r.sums;
o.sumv = l.addv * l.sums + l.sumv + o.val + r.addv * r.sums + r.sumv;
return ;
}
void builds(int& u, int pa, int L, int R) {
if(L > R){ u = 0; return ; }
int M = L + R >> 1;
Node& o = nodes[u = ++ToT];
o.sym = o.sums = ds[M];
o.val = o.sumv = dv[M]; o.addv = 0;
ids[M] = u; fa[u] = pa;
builds(ch[u][0], u, L, M - 1); builds(ch[u][1], u, M + 1, R);
maintain(u);
return ;
}
void pushdown(int u) {
Node &o = nodes[u], &l = nodes[ch[u][0]], &r = nodes[ch[u][1]];
if(o.addv) {
// printf("%lld ", o.sumv);
o.sumv += o.addv * o.sums; o.val += o.addv * o.sym;
l.addv += o.addv; r.addv += o.addv;
// printf("pushdown: (u)%d (o.addv)%lld (o.sumv)%lld\n", u, o.addv, o.sumv);
o.addv = 0;
}
nodes[0].clear();
return ;
}
void rotate(int u) {
int y = fa[u], z = fa[y], l = 0, r = 1;
if(ch[y][1] == u) swap(l, r);
if(z) ch[z][ch[z][1]==y] = u;
fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
ch[y][l] = ch[u][r]; ch[u][r] = y;
maintain(y); maintain(u);
return ;
}
int St[maxn], top;
void splay(int u) {
int t = u;
while(fa[t]) St[++top] = t, t = fa[t];
St[++top] = t;
while(top) pushdown(St[top--]);
while(fa[u]) {
int y = fa[u], z = fa[y];
if(!z) rotate(u);
else {
if((ch[z][0] == y) ^ (ch[y][0] == u)) rotate(u);
else rotate(y);
rotate(u);
}
}
return ;
}
int splitl(int u) {
splay(u);
int ret = ch[u][0];
Node &o = nodes[u], &l = nodes[ch[u][0]];
nodes[0].clear();
o.sumv -= (l.sumv + l.addv * l.sums);
fa[ret] = 0; ch[u][0] = 0;
return ret;
}
int splitr(int u) {
splay(u);
int ret = ch[u][1];
Node &o = nodes[u], &r = nodes[ch[u][1]];
nodes[0].clear();
o.sumv -= (r.sumv + r.addv * r.sums);
fa[ret] = 0; ch[u][1] = 0;
return ret;
}
void update(int u, int v) {
splay(u);
nodes[u].addv += v;
return ;
}
void merge(int a, int b) {
if(!a || !b) return ;
while(ch[a][1]) a = ch[a][1];
while(ch[b][0]) b = ch[b][0];
splay(a); splay(b);
Node &o = nodes[b], &l = nodes[a];
o.sumv += (l.sumv + l.addv * l.sums);
fa[a] = b; ch[b][0] = a;
return ;
} int main() {
n = read();
for(int i = 2; i <= n; i++) AddEdge(i, read());
for(int i = 1; i <= n; i++) val[i] = read(); build(1, 0);
builds(root, 0, 1, n << 1);
int q = read();
while(q--) {
char tp = Getchar();
while(!isalpha(tp)) tp = Getchar();
if(tp == 'Q') {
int x = dl[read()];
splay(ids[x]);
Node& o = nodes[ids[x]], &r = nodes[ch[ids[x]][1]];
LL ans = o.sumv + o.addv * o.sums - r.sumv - r.addv * r.sums;
printf("%lld\n", ans);
}
if(tp == 'F') {
int x = read(), v = read(), xl = dl[x], xr = dr[x], lt = 0, rt = 0;
lt = splitl(ids[xl]);
rt = splitr(ids[xr]);
update(ids[xl], v);
merge(lt, ids[xl]);
merge(ids[xr], rt);
}
if(tp == 'C') {
int x = read(), fax = read(), xl = dl[x], xr = dr[x], lt, rt;
lt = splitl(ids[xl]);
rt = splitr(ids[xr]);
merge(lt, rt);
rt = splitr(ids[dl[fax]]);
lt = ids[dl[fax]];
merge(lt, ids[xl]);
merge(ids[xr], rt);
}
} return 0;
}

不知为何慢的飞起,会不会是因为我用的数组版 splay?求帮忙优化orz。