SPOJ2666-QTREE4

这题是可以点分治或LCT作的。。但这里讲边分治的做法。。

和点分治类似,边分治利用一条树上路径要吗经过一条边,要么不经过。而不经过的路径必然会在一次分治中变为经过的。

我们找到一条中心边,边左边和边右边分别建一个大根堆。左边的堆中存储边的左子树中白点的深度,右边的堆储存右边的。

当发生颜色反转时,若为黑点转白点,则去掉标记,并将点压入相应的堆中。若为白点转为黑点,则将其打上标记。

统计时,先不断弹左右堆顶,直到堆顶没有标记或堆空。然后用左堆顶值、右堆顶值和(左堆顶值+右堆顶值+中心边长度)三者的最大值更新答案。

为方便边分治,我们使每个点的度数不大于$3$,即转为一棵二叉树。

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 2e5 + 5, MAXE = 4e6 + 5;

struct {
int v, w, nxt, pre;
} edge[MAXE], _edge[MAXE];

int head[MAXN], _head[MAXN], tot, _tot, tail[MAXN], mark[MAXN], size[MAXN], n,
_n, cnt, rt, midedge, maxv;

inline void _add(int u, int v, int w) {
_edge[_tot].v = v, _edge[_tot].w = w, _edge[_tot].nxt = _head[u],
_head[u] = _tot++;
}

inline void add(int u, int v, int w) {
edge[tot].v = v, edge[tot].w = w, edge[tot].nxt = head[u], head[u] = tot++;
}

inline void del(int u, int i) {
if (head[u] == i)
head[u] = edge[i].nxt;
else
edge[edge[i].pre].nxt = edge[i].nxt;
if (tail[u] == i)
tail[u] = edge[i].pre;
else
edge[edge[i].nxt].pre = edge[i].pre;
}

inline void build(int u, int fa) {
int father = 0;
for (int i = _head[u]; ~i; i = _edge[i].nxt) {
int v = _edge[i].v, w = _edge[i].w;
if (v == fa)
continue;
if (father == 0)
add(u, v, w), add(v, u, w), father = u, build(v, u);
else
mark[++n] = 0, add(n, father, 0), add(father, n, 0), father = n,
add(v, father, w), add(father, v, w), build(v, u);
}
}

inline void get_pre() {
memset(tail, -1, sizeof(tail));
for (int i = 1; i <= n; i++)
for (int j = head[i]; ~j; j = edge[j].nxt)
edge[j].pre = tail[i], tail[i] = j;
}

struct point {
int u, dis;
point() {}
point(int _u, int _dis) {
u = _u;
dis = _dis;
}
bool operator<(const point &_A) const { return dis < _A.dis; }
};

struct tree {
int rt, midlen, ans, ls, rs;
priority_queue<point> q;
} t[2 * MAXN];

inline void dfs_size(int u, int fa, int dir) {
_add(u, rt, dir);
if (mark[u])
t[rt].q.push(point(u, dir));
size[u] = 1;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v, w = edge[i].w;
if (v == fa)
continue;
dfs_size(v, u, dir + w), size[u] += size[v];
}
}

inline void dfs_midedge(int u, int fa) {
if (max(size[u], size[t[rt].rt] - size[u]) < maxv)
maxv = max(size[u], size[t[rt].rt] - size[u]), midedge = fa;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if (i != (fa ^ 1))
dfs_midedge(v, i);
}
}

inline void pushup(int rt) {
t[rt].ans = -1;
for (; !t[rt].q.empty() && mark[t[rt].q.top().u] == 0;)
t[rt].q.pop();
int ls = t[rt].ls, rs = t[rt].rs;
if (ls == 0 && rs == 0) {
if (mark[t[rt].rt])
t[rt].ans = 0;
} else {
if (t[ls].ans > t[rt].ans)
t[rt].ans = t[ls].ans;
if (t[rs].ans > t[rt].ans)
t[rt].ans = t[rs].ans;
if (!t[ls].q.empty() && !t[rs].q.empty())
t[rt].ans =
max(t[rt].ans, t[ls].q.top().dis + t[rs].q.top().dis + t[rt].midlen);
}
}

inline void dfs(int id, int u) {
rt = id, maxv = n, midedge = -1, t[id].rt = u;
dfs_size(u, 0, 0), dfs_midedge(u, -1);
if (~midedge) {
int p1 = edge[midedge].v, p2 = edge[midedge ^ 1].v;
t[id].midlen = edge[midedge].w, t[id].ls = ++cnt, t[id].rs = ++cnt,
del(p1, midedge ^ 1), del(p2, midedge), dfs(t[id].ls, p1),
dfs(t[id].rs, p2);
}
pushup(id);
}

inline void update(int u) {
mark[u] ^= 1;
for (int i = _head[u]; ~i; i = _edge[i].nxt) {
int v = _edge[i].v, w = _edge[i].w;
if (mark[u] == 1)
t[v].q.push(point(u, w));
pushup(v);
}
}

int main() {
memset(_head, -1, sizeof(_head)), scanf("%d", &_n);
for (int i = 1, u, v, w; i < _n; i++)
scanf("%d%d%d", &u, &v, &w), _add(u, v, w), _add(v, u, w);
memset(head, -1, sizeof(head)), n = _n;
for (int i = 1; i <= n; i++)
mark[i] = 1;
build(1, 0), get_pre(), memset(_head, -1, sizeof(_head)), _tot = 0,
dfs(cnt = 1, 1);
char op[2];
int m, x;
scanf("%d", &m);
for (; m; --m) {
scanf("%s", op);
if (op[0] == 'A')
if (t[1].ans == -1)
printf("They have disappeared.\n");
else
printf("%d\n", t[1].ans);
else
scanf("%d", &x), update(x);
}
return 0;
}

   转载规则


《SPOJ2666-QTREE4》 VinovnyyPoterpevshiy 采用 知识共享署名 4.0 国际许可协议 进行许可。
 本篇
SPOJ2666-QTREE4 SPOJ2666-QTREE4
这题是可以点分治或LCT作的。。但这里讲边分治的做法。。 和点分治类似,边分治利用一条树上路径要吗经过一条边,要么不经过。而不经过的路径必然会在一次分治中变为经过的。 我们找到一条中心边,边左边和边右边分别建一个大根堆。左边的堆中存储边的左
2019-09-22
下一篇 
SPOJ1825-FTOUR2 SPOJ1825-FTOUR2
我们知道,树上两个点的LCA要么是当前根节点,要么不是。。所以两个点间的最短路径要么经过当前根节点,要么在一棵当前根节点的子树中。。 考虑点分治,于是在原来同一子树中的两个点必然在一次分治中变为路径经过当前根节点的两个点。 点分治标准开头(
2019-09-22
  目录