BZOJ 3991: [SDOI2015]寻宝游戏

Description

 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物

Input

 第一行,两个整数N、M,其中M为宝物的变动次数。

接下来的N-1行,每行三个整数x、y、z,表示村庄x、y之间有一条长度为z的道路。

接下来的M行,每行一个整数t,表示一个宝物变动的操作。若该操作前村庄t内没有宝物,则操作后村庄内有宝物;若该操作前村庄t内有宝物,则操作后村庄内没有宝物。

Output

 M行,每行一个整数,其中第i行的整数表示第i次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出0。

Sample Input

4 5

1 2 30

2 3 50

2 4 60

2

3

4

2

1

Sample Output

0

100

220

220

280

HINT

 1<=N<=100000

1<=M<=100000

对于全部的数据,1<=z<=10^9

Solution

题目大意:给你一棵树,有把一个点染黑/染白两种操作。要求你时刻维护遍历所有黑点距离最短是多少。

先求出各个点的DFS序,dfn(x)。

维护黑点的DFS序。我们定义某点x的左右两点u,v为dfn(u) <= dfn(x) <= dfn(v), u ∈ Black, v ∈ Black。

对于染黑某个点的操作:答案加上这个点到左右两点的距离减去左右两点的距离。如图:

ans = ans + dis(u, x) + dis(x, v) – dis(u, v);

对于染白某个点的操作:答案减去这个点到左右两点的距离加上左右两点的距离。如图:

ans = ans – dis(u, x) – dis(x, v) + dis(u, v);

用平衡树维护dfs序就好了。如果你不想写平衡树,也可以用STL里面的set实现。

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <cstdio>
#include <set>
using namespace std;
const int INF = 1<<30;
typedef long long LL;
int getint() {
	int r = 0, k = 1; char c = getchar();
	for (; '0' > c || c > '9'; c = getchar()) if (c == '-') k = -1;
	for (; '0' <= c && c <= '9'; c = getchar()) r = r * 10 - '0' + c;
	return r * k;
}
set<int> S;
set<int>::iterator it, fst, lst;
int N, M;
int tot = 0;
bool have[100005];
struct edge_type {
	int to, next, len;
} edge[200005];
int h[100005], cnte = 1;
void ins(int u, int v, int w) {
	edge[++cnte].to = v;
	edge[cnte].next = h[u];
	edge[cnte].len = w;
	h[u] = cnte;
	edge[++cnte].to = u;
	edge[cnte].next = h[v];
	edge[cnte].len = w;
	h[v] = cnte;
}
int fa[100005], dep[100005], siz[100005], son[100005], top[100005], dfn[100005], dfsclock = 0, ddfn[100005];
LL dis[100005];
int dfs1(int now, int father, int deep, LL ndis) {
	dfn[now] = ++dfsclock; ddfn[dfsclock] = now;
	fa[now] = father; dep[now] = deep; dis[now] = ndis;
	siz[now] = 1; son[now] = 0;
	for (int i = h[now]; i; i = edge[i].next) {
		if (edge[i].to == father) continue;
		siz[now] += dfs1(edge[i].to, now, deep + 1, ndis+edge[i].len);
		if (siz[son[now]] < siz[edge[i].to]) son[now] = edge[i].to;
	}
	return siz[now];
}
void dfs2(int now, int father, int tp) {
	top[now] = tp;
	if (son[now]) {
		dfs2(son[now], now, tp);
		for (int i = h[now]; i; i = edge[i].next) {
			if (edge[i].to == father || edge[i].to == son[now]) continue;
			dfs2(edge[i].to, now, edge[i].to);
		}
	}
}
int lca(int u, int v) {
	while (top[u] != top[v]) {
		if (dep[top[u]] < dep[top[v]]) swap(u, v);
		u = fa[top[u]];
	}
	if (dep[u] > dep[v]) swap(u, v);
	return u;
}
LL dist(int u, int v) { return dis[u] + dis[v] - (dis[lca(u, v)] << 1); }
int main() {
	N = getint(); M = getint();
	int x,y,z;
	for (int i=1;i<N;++i) {
		x = getint();y=getint(); z = getint();
		ins(x, y, z);
	}
	dfs1(1, 0, 1, 0);
	dfs2(1, 0, 1);
	LL ans = 0;
	for (int i = 0; i < M; ++i) {
		x = getint();
		x = dfn[x];
		if (have[x]) {
			have[x] = false;
			it = S.find(x);
			--tot;
			if (tot) {
				lst = it; if (lst == S.begin()) lst = S.end(); --lst;
				fst = it; ++fst; if (fst == S.end()) fst = S.begin();
				ans = ans - dist(ddfn[x], ddfn[*lst]) - dist(ddfn[x], ddfn[*fst]) + dist(ddfn[*fst], ddfn[*lst]);
			}
			S.erase(it);
		} else {
			have[x] = true;
			it = (S.insert(x)).first;
			if (tot) {
				lst = it; if (lst == S.begin()) lst = S.end(); --lst;
				fst = it; ++fst; if (fst == S.end()) fst = S.begin();
				ans = ans + dist(ddfn[x], ddfn[*lst]) + dist(ddfn[x], ddfn[*fst]) - dist(ddfn[*fst], ddfn[*lst]);
			}
			++tot;
		}
		printf("%lld\n", ans);
	}
}