1. 程式人生 > 實用技巧 >P3345 [ZJOI2015]幻想鄉戰略遊戲

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\),那麼遍歷它所有的兒子\(y\),以\(y\)作為補給點找答案,哪個小就往那裡走。如果所有\(y\)\(ans\)都大於以\(x\)為補給點的\(ans\),那麼\(x\)就是當前最合適的補給點。

​ 假設現在以\(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;
}