P3345 [ZJOI2015]幻想鄉戰略遊戲
題目描述
傲嬌少女幽香正在玩一個非常有趣的戰略類遊戲,本來這個遊戲的地圖其實還不算太大,幽香還能管得過來,但是不知道為什麼現在的網遊廠商把遊戲的地圖越做越大,以至於幽香一眼根本看不過來,更別說和別人打仗了。 在打仗之前,幽香現在面臨一個非常基本的管理問題需要解決。 整個地圖是一個樹結構,一共有 n 塊空地,這些空地被 n−1 條帶權邊連線起來,使得每兩個點之間有一條唯一的路徑將它們連線起來。
在遊戲中,幽香可能在空地上增加或者減少一些軍隊。同時,幽香可以在一個空地上放置一個補給站。 如果補給站在點 u 上,並且空地 v 上有 dv 個單位的軍隊,那麼幽香每天就要花費 dv×dist(u,v) 的金錢來補給這些軍隊。由於幽香需要補給所有的軍隊,因此幽香總共就要花費為 ∑(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≤10^3,0≤∣e∣≤10^3,1≤n≤10^5,1≤Q≤10^5\)
非常神奇的是,對於所有資料,這棵樹上的點的度數都不超過 20。
動態點分治的題。
\(sum_x\):表示\(x\)這顆樹內的\(d\)值之和。
\(dum_x\):表示\(x\)這顆樹內的\(d *dis(x, y)\)值之和。
\(fum_x\):表示\(x\)這顆樹內的\(d *dis(fa[x], y)\)值之和。
(其中\(y\)為\(x\)的子樹內的節點,\(fa[x]\)為\(x\)在點分樹上的父親)
我們可以發現其實可以通過貪心的方法找到最小的\(ans\)
假設現在以\(x\)點為補給點,計算\(ans\)的方法:
long long calc(int x) {
long long res = dum[x];
for(int i = x; fa[i]; i = fa[i])
res += dum[fa[i]] - fum[i] + (sum[fa[i]] - sum[i]) * dist(fa[i], x); //畫個圖就好理解了
return res;
}
\(dum[fa[i]] - fum[i]\)是綠色部分的點\(y\)到\(fa[x]\)的\(d * dis(y, fa[x])\)值之和,\((sum[fa[i]] - sum[i]) * dist(fa[i], x)\)是綠色部分的點\(y\)的\(d\)值之和乘上\(dis(fa[x], x)\),所以總體就是綠色部分的所有點到\(x\)的\(d * dis(y, x)\)值之和。
求\(dis(x, y)\)要用\(lca\)來求,求\(lca\)要用倍增的方法,這樣時間複雜度低一點。
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 5e5 + 5, inf = 1e9;
int n, m, rt, cnt, tot, root, totsize;
int f[N][23], fa[N], vst[N], de[N << 2], dep[N], dis[N], vis[N << 2], lg[N << 1], dfn[N], siz[N], link[N << 1], max_siz[N], head[N];
long long sum[N], dum[N], fum[N];
struct edge { int to, nxt, val; } e[N << 1];
void add(int x, int y, int z) {
e[++cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].val = z;
}
void get_root(int x, int Fa) {
siz[x] = 1; max_siz[x] = 0;
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == Fa || vst[y]) continue;
get_root(y, x);
siz[x] += siz[y];
max_siz[x] = max(max_siz[x], siz[y]);
}
max_siz[x] = max(max_siz[x], totsize - siz[x]);
if(max_siz[root] > max_siz[x]) root = x;
}
void build(int x, int Fa) {
vst[x] = 1;
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == Fa || vst[y]) continue;
max_siz[root = 0] = totsize = siz[y];
get_root(y, x); fa[root] = x; link[i] = root;
build(root, x);
}
}
void dfs_pre(int x, int Fa) {
dfn[x] = ++ tot; vis[tot] = x;
dep[x] = dep[Fa] + 1; de[tot] = dep[x];
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == Fa) continue;
dis[y] = dis[x] + e[i].val;
dfs_pre(y, x);
vis[++tot] = x; de[tot] = dep[x];
}
}
void RMQ() {
lg[0] = -1;
for(int i = 1;i <= tot; i++) lg[i] = lg[i >> 1] + 1;
for(int i = 1;i <= tot; i++) f[i][0] = i;
for(int i = 1;i <= 21; i++) {
if((1 << i) > tot) break;
for(int j = 1;j <= tot; j++) {
if(j + (1 << i) - 1 > tot) break;
int a = f[j][i - 1], b = f[j + (1 << (i - 1))][i - 1];
f[j][i] = de[a] < de[b] ? a : b;
}
}
}
int LCA(int x, int y) {
int l = dfn[x], r = dfn[y];
if(l > r) swap(l, r);
int k = lg[r - l + 1];
int a = f[l][k], b = f[r - (1 << k) + 1][k];
return de[a] < de[b] ? vis[a] : vis[b];
}
long long dist(int x, int y) {
int lca = LCA(x, y);
return dis[x] + dis[y] - 2 * dis[lca];
}
void change(int x, int _3k) {
int y = x;
while(x) {
sum[x] += _3k;
dum[x] += 1ll * _3k * dist(x, y);
fum[x] += 1ll * _3k * dist(y, fa[x] ? fa[x] : x);
x = fa[x];
}
}
long long calc(int x) {
long long res = dum[x];
for(int i = x; fa[i]; i = fa[i])
res += dum[fa[i]] - fum[i] + (sum[fa[i]] - sum[i]) * dist(fa[i], x);
return res;
}
long long query() {
int x = rt, y;
long long res, tmp;
while(1) {
tmp = calc(x); y = x;
for(int i = head[x]; i ; i = e[i].nxt) {
int to = e[i].to;
res = calc(to);
if(res < tmp) {
tmp = res; y = link[i]; break;
}
}
if(x == y) break; x = y;
}
return tmp;
}
signed main() {
// freopen("b.out","w",stdout);
n = read(); m = read();
for(int i = 1, x, y, z;i <= n - 1; i++) {
x = read(); y = read(); z = read();
add(x, y, z); add(y, x, z);
}
max_siz[root] = totsize = n;
get_root(1, 0);
rt = root; build(root, 0);
dfs_pre(1, 0); RMQ();
for(int i = 1, x, y;i <= m; i++) {
x = read(); y = read();
change(x, y);
printf("%lld\n", query());
}
return 0;
}