题目描述
傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。
在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。
在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv*dist(u,v)的金钱来补给这些军队。
由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。
因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。
但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。
输入输出格式
输入格式:
第一行两个数n和Q分别表示树的点数和幽香操作的个数,其中点从1到n标号。 接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队(如果e<0,就相当于是幽香在u上减少了|e|单位个军队,说白了就是du←du+e)。数据保证任何时刻每个点上的军队数量都是非负的。
输出格式:
对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。
输入输出样例
输入样例#1:
10 5
1 2 1
2 3 1
2 4 1
1 5 1
2 6 1
2 7 1
5 8 1
7 9 1
1 10 1
3 1
2 1
8 1
3 1
4 1
输出样例#1:
0
1
4
5
6
说明
对于所有数据,1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5 非常神奇的是,对于所有数据,这棵树上的点的度数都不超过20,且N,Q>=1
分析:
感觉真的很毒瘤啊。
题目要求带修改的树的带权重心。
考虑建出点分树,然后维护当前点的size及儿子的size,判断时先判断当前点是不是重心,如果不是再走向最大的儿子。
代码:
// luogu-judger-enable-o2
#include <cstdio>
#include <vector>
typedef long long i64;
const int N = 100000 + 10, E = N * 2, LOG = 20;
int n, tcase, root;
i64 tot = 0;
struct edge_t {
int adj, cost;
edge_t *next;
} *e[N], pool[E];
void link(int a, int b, int c) {
static edge_t *tot = pool;
tot->adj = b;
tot->next = e[a];
tot->cost = c;
e[a] = tot++;
}
bool forbid[N];
int Centroid(int s) {
static std::vector<int> q;
static int size[N], fa[N];
q.clear();
q.push_back(s);
fa[s] = 0;
for (int i = 0; i < q.size(); ++i) {
int a = q[i];
size[a] = 1;
for (edge_t *it = e[a]; it; it = it->next) {
int b = it->adj;
if (!forbid[b] && b != fa[a]) {
q.push_back(b);
fa[b] = a;
}
}
}
for (int i = q.size() - 1; i >= 0; --i) size[fa[q[i]]] += size[q[i]];
int tot = size[s];
for (int i = 0; i < q.size(); ++i) {
int a = q[i];
bool flag = (tot - size[a]) <= tot / 2;
for (edge_t *it = e[a]; flag && it; it = it->next) {
int b = it->adj;
if (!forbid[b] && b != fa[a]) flag &= (size[b] <= tot / 2);
}
if (flag)
return a;
}
return s;
}
int anc[N], from[N];
std::vector<int> succ[N];
int Divide(int a) {
forbid[a = Centroid(a)] = true;
for (edge_t *it = e[a]; it; it = it->next) {
int b = it->adj;
if (!forbid[b]) {
int c = Divide(b);
succ[a].push_back(c);
anc[c] = a;
from[c] = b;
}
}
return a;
}
int fa[N], dep[N], left[N];
i64 sum[N], dist[N], f[N], sub[N];
std::vector<int> dfn, table[LOG];
void DFS(int a) {
left[a] = dfn.size();
dfn.push_back(a);
for (edge_t *it = e[a]; it; it = it->next) {
int b = it->adj;
if (b != fa[a]) {
fa[b] = a;
dep[b] = dep[a] + 1;
dist[b] = dist[a] + it->cost;
DFS(b);
dfn.push_back(a);
}
}
}
void BuildSparseTable() {
table[0] = dfn;
for (int i = 1; i < LOG; ++i) {
table[i].resize(dfn.size());
for (int j = 0; j + (1 << (i - 1)) <= dfn.size(); ++j) {
int a = table[i - 1][j], b = table[i - 1][j + (1 << (i - 1))];
table[i][j] = (dep[a] < dep[b] ? a : b);
}
}
}
inline int lg2(int x) { return 31 - __builtin_clz(x); }
int LCA(int a, int b) {
a = left[a], b = left[b];
if (a > b) std::swap(a, b);
int layer = lg2(b - a + 1);
int u = table[layer][a], v = table[layer][b - (1 << layer) + 1];
return dep[u] < dep[v] ? u : v;
}
i64 Dist(int u, int v) { return dist[u] + dist[v] - 2 * dist[LCA(u, v)]; }
int FindCentroid(int a) {
for (std::vector<int>::iterator it = succ[a].begin(); it != succ[a].end(); ++it) {
int b = *it;
if (sum[b] > tot / 2) {
i64 out = sum[a] - sum[b];
for (int t = from[b]; t; t = anc[t]) sum[t] += out;
int res = FindCentroid(b);
for (int t = from[b]; t; t = anc[t]) sum[t] -= out;
return res;
}
}
return a;
}
i64 Solve(int u, int e) {
tot += e;
for (int a = u; a; a = anc[a]) {
sum[a] += e;
if (anc[a]) sub[a] += e * Dist(u, anc[a]);
f[a] += e * Dist(u, a);
}
int c = FindCentroid(root);
i64 res = 0;
for (int a = c, b = 0; a; b = a, a = anc[a]) {
res += (f[a] - sub[b]) + (sum[a] - sum[b]) * Dist(c, a);
}
return res;
}
int main() {
scanf("%d%d", &n, &tcase);
for (int cnt = n - 1, a, b, c; cnt--;) {
scanf("%d%d%d", &a, &b, &c);
link(a, b, c);
link(b, a, c);
}
root = Divide(1);
DFS(1);
BuildSparseTable();
while (tcase--) {
int u, e;
scanf("%d%d", &u, &e);
printf("%lld\n", Solve(u, e));
}
return 0;
}