P3345 [ZJOI2015]幻想乡战略游戏

动态维护,加权点重心。

假设当前决策点为$u$,$v$为其子结点。

显然只有一个$v$可能满足。

此时$ans$在$v$的子树里!看到这个想到点分树。

  • 需要在原来的树结构上寻找决策点,而不是点分树的。
  • 进入下一个子树,显然 可以用点分树的子树。

可以知道答案不会转移过多,所以只需啊计算$\sum dis(i,v)\times d_i$

这个时候就是最普通的点分树了,一层一层往上统计,去除重复的。

复杂度$O(n\log^2n)$

代码
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pii pair<int, ll>
#define mk make_pair
const int N = 2e5 + 10;
const int mod = 1e9 + 7;

int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + c - '0', c = getchar();
return x * f;
}
vector<pii> g[N];
vector<pii> dg[N];
int dfn[N], st[N][20], tot, dep[N];
int lg2[N];
void dfs1(int x, int fa, int d)
{
st[++tot][0] = x;
dep[x] = d;
dfn[x] = tot;
for (pii now : g[x])
{
int to = now.first;
if (to == fa)
continue;
dfs1(to, x, d + now.second);
st[++tot][0] = x;
}
}
int lower(int u, int v)
{
return (dep[u] < dep[v]) ? u : v;
}
void Lcainit()
{
dfs1(1, 0, 0);
for (int i = 2; i <= tot; i++)
lg2[i] = lg2[i >> 1] + 1;
for (int l = 1; (1 << l) <= tot; l++)
{
for (int i = 1; i + (1 << l) - 1 <= tot; i++)
st[i][l] = lower(st[i][l - 1], st[i + (1 << (l - 1))][l - 1]);
}
}
int lca(int u, int v)
{
u = dfn[u];
v = dfn[v];
if (u > v)
swap(u, v);
int i = lg2[v - u + 1], w = (1 << i);
return lower(st[u][i], st[v - w + 1][i]);
}
ll dis(int u, int v)
{
return dep[u] + dep[v] - 2 * dep[lca(u, v)];
}
int siz[N], f[N], vis[N], fa[N];

int gcnt, rt;
void GetRoot(int x, int fa)
{
siz[x] = 1;
f[x] = 0;
for (pii now : g[x])
{
int to = now.first;
if (to == fa || vis[to])
continue;
GetRoot(to, x);
siz[x] += siz[to];
f[x] = max(f[x], siz[to]);
}
f[x] = max(f[x], gcnt - siz[x]);
if (f[x] < f[rt])
rt = x;
}
void dfs2(int x)
{
vis[x] = 1;
for (pii now : g[x])
{
int to = now.first;
if (vis[to])
continue;
gcnt = siz[to];
rt = 0;
GetRoot(to, x);
dg[x].push_back(mk(to, rt));

fa[rt] = x;
dfs2(rt);
}
}
ll sum[N];
ll sumf[N];
ll sumd[N];

void update(int x, int val)
{

sum[x] += val;
for (int i = x; fa[i]; i = fa[i])
{

ll len = dis(x, fa[i]);

sum[fa[i]] += val;
sumd[fa[i]] += val * len;
sumf[i] += val * len;
}
}
ll calc(int x)
{
ll res = sumd[x];

for (int i = x; fa[i]; i = fa[i])
{
ll len = dis(x, fa[i]);
res += len * (sum[fa[i]] - sum[i]);

res += (sumd[fa[i]] - sumf[i]);
}
return res;
}
ll query(int x)
{

ll tmp = calc(x);
for (pii now : dg[x])
{
int to = now.first;
if (calc(to) < tmp)
return query(now.second);
}
return tmp;
}
int main()
{
int n = read(), q = read();
for (int i = 1; i < n; i++)
{
int u = read(), v = read(), w = read();
g[u].push_back(mk(v, w));
g[v].push_back(mk(u, w));
}
Lcainit();
rt = 0;
f[0] = 1e9;
gcnt = n;
GetRoot(1, 0);
int R = rt;
dfs2(rt);

while (q--)
{
int x = read(), y = read();

update(x, y);

printf("%lld\n", query(R));
}
}