[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.
输出
输入示例
3 1 1 4 5 7 5 Q 2 F 1 3 Q 2 C 2 3 Q 2
输出示例
9 15 25
数据规模及约定
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。