(color{#0066ff}{ 题目描述 })
傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。
在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。
在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv*dist(u,v)的金钱来补给这些军队。
由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。
因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。
但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。
(color{#0066ff}{输入格式})
第一行两个数n和Q分别表示树的点数和幽香操作的个数,其中点从1到n标号。 接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队(如果e<0,就相当于是幽香在u上减少了|e|单位个军队,说白了就是du←du+e)。数据保证任何时刻每个点上的军队数量都是非负的。
(color{#0066ff}{输出格式})
对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。
(color{#0066ff}{输入样例})
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
(color{#0066ff}{输出样例})
0
1
4
5
6
(color{#0066ff}{数据范围与提示})
对于所有数据,1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5 非常神奇的是,对于所有数据,这棵树上的点的度数都不超过20,且N,Q>=1
(color{#0066ff}{题解})
这题既然给出了大方的6s,一定是道巨暴力题
动态点分治题
建立点分树,每个点维护三个值
gather[i]代表点分树中以i为根子树的所有点的军队数量 * 该点到i的dis(注意是原树上的dis)
tofa[i]表示点分树中以i为根的子树对它在点分树上的父亲的gather的贡献
tot[i]代表点分树中以i为根的子树中的军队数量
考虑当前补给站在a, 考虑移动到a的儿子b
那么a除了b的所有子树的军队都会加上一个(dis_{ab})
b的子树内所有的军队都会减去一个(dis_{ab})
可以发现,如果某个点更优,当且仅当外面的军队少,里面的军队多
所以一旦发现b更优,那么最后的补给站一定在b子树内
所以对于询问,我从点分树的根开始找,一旦发现孩子更优,就在孩子的子树中递归
现在考虑怎么计算一个点的答案(就是补给站在那个点的答案)
首先,初始答案是gather[i],这是子树内的贡献
现在考虑怎么统计子树外的点的贡献
设dis为当前点到补给站的距离(原树距离)
用tot[fa[i]] - tot[i]就是父亲子树中出去当前子树的部分的军队数量
用这个乘上一个dis,就是父亲与当前点的路径的贡献
这是父亲到当前点的,还有一段是军队到父亲的(军队到父亲+父亲到当前点=军队到当前点)
这部分为gather[fa[i]] - tofa[i]父亲的总贡献- 当前子树贡献就是其它点到父亲的贡献
一直统计就行了
对于修改,在点分树上向上跳(log)然后直接维护三个值就行了
(O(nlog^2n))
这是写欧拉序RMQ查询距离的复杂度
如果写倍增,那复杂度就是(O(nlog^3n)),会TLE
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; int x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
struct node {
int to;
LL dis;
node *nxt;
node(int to = 0, LL dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
void *operator new (size_t) {
static node *S = NULL, *T = NULL;
return (S == T) && (T = (S = new node[1024]) + 1024), S++;
}
};
const int maxn = 2e5 + 10;
int siz[maxn], f[maxn], root, sum, n, q, rt, fa[maxn], lg[maxn];
LL gather[maxn], tofa[maxn],tot[maxn];
LL st[maxn][26], dfn[maxn], euler, Dis[maxn];
bool vis[maxn];
node *h[maxn], *head[maxn];
void add(int from, int to, int dis, node **hh) {
hh[from] = new node(to, dis, hh[from]);
}
void dfs(int x, int F, LL dis) {
Dis[x] = dis;
st[++euler][0] = dis, dfn[x] = euler;
for(node *i = h[x]; i; i = i->nxt) {
if(i->to == F) continue;
dfs(i->to, x, dis + i->dis);
st[++euler][0] = dis;
}
}
void ST() {
dfs(1, 0, 0);
lg[0] = -1;
for(int i = 1; i < maxn; i++) lg[i] = lg[i >> 1] + 1;
for(int j = 1; (1 << j) <= euler; j++)
for(int i = 1; i + (1 << j) - 1 <= euler; i++)
st[i][j] = std::min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
LL getdis(int x, int y) {
if(dfn[x] > dfn[y]) std::swap(x, y);
int len = lg[dfn[y] - dfn[x] + 1];
return Dis[x] + Dis[y] - (std::min(st[dfn[x]][len], st[dfn[y] - (1 << len) + 1][len]) << 1);
}
void getroot(int x, int F) {
siz[x] = 1;
f[x] = 0;
for(node *i = h[x]; i; i = i->nxt) {
if(i->to == F || vis[i->to]) continue;
getroot(i->to, x);
siz[x] += siz[i->to];
f[x] = std::max(f[x], siz[i->to]);
}
f[x] = std::max(f[x], sum - siz[x]);
if(f[x] < f[root]) root = x;
}
void build(int x) {
vis[x] = true;
for(node *i = h[x]; i; i = i->nxt) {
if(vis[i->to]) continue;
sum = siz[i->to], root = 0;
getroot(i->to, x);
add(x, root, i->to, head);
fa[root] = x;
build(root);
}
}
void add(int pos, LL val) {
tot[pos] += val;
for(int i = pos; fa[i]; i = fa[i]) {
LL D = getdis(pos, fa[i]);
tofa[i] += D * val;
tot[fa[i]] += val;
gather[fa[i]] += D * val;
}
}
LL calc(int x) {
LL res = gather[x];
for(int i = x; fa[i]; i = fa[i]) {
LL D = getdis(fa[i], x);
res += (tot[fa[i]] - tot[i]) * D;
res += gather[fa[i]] - tofa[i];
}
return res;
}
LL query(int x) {
LL res = calc(x);
for(node *i = head[x]; i; i = i->nxt)
if(calc(i->dis) < res) return query(i->to);
return res;
}
signed main() {
n = in(), q = in();
int x, y, z;
for(int i = 1; i < n; i++) {
x = in(), y = in(), z = in();
add(x, y, z, h), add(y, x, z, h);
}
f[0] = sum = n;
getroot(1, 0);
int rt = root;
build(root);
ST();
while(q --> 0) {
x = in(), y = in();
add(x, y);
printf("%lld
", query(rt));
}
return 0;
}