[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的事件,输出一行一个整数,表示此次实验的收集器最终能量。

输入示例

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。

posted @ 2016-08-05 15:33  xjr01  阅读(180)  评论(0编辑  收藏  举报