CF2006B Iris and the Tree

空白菌發表於2024-09-23

題目連結

題解

知識點:DFS序。

編號已經滿足 dfs 序,因此對於邊 \(t_v = (u,v), u < v\) ,有且僅有兩條路徑 \(v - 1 \to v, R_v \to R_v \bmod n + 1\) 會經過這條邊,前者是進入子樹 \(v\) 時經過,後者是離開子樹 \(v\) 時經過。其中 \(R_v\) 表示子樹 \(v\) 內的最大編號,我們可以用 dfs 預處理。

顯然,當一條路徑存在未確定的邊時,它的距離等於路徑上確定的邊的邊權和加上剩餘未分配的權值(全給未確定的邊即可)。

設總和為 \(ans\) ,最初 \(n\) 條路徑的距離都是 \(0 +w\) ,因此 \(ans = nw\)

每次確定一個邊權 \(y\),未分配的權值將會減少 \(y\),因此所有不經過這條邊且存在未確定邊的路徑的距離都將減少 \(y\),而經過這條邊的路徑的距離不會發生變化。設剩餘存在未確定邊的路徑數為 \(rest\) ,那麼一次操作後答案將更新為 \(ans - (rest - 2)y\)

此外,一次操作後,如果存在路徑已經完全確定時,它們的距離就不能加上未分配的權值,假設 \(w'\) 是當前未分配的權值,那麼需要將答案更新為 \(ans - w\)

最後,我們可以預處理每條路徑未確定邊的個數 \(cnt\) ,每次確定一條邊,會更新經過它的兩條路徑的 \(cnt\) 值。當某條路徑的 \(cnt\) 歸零,表示它已經完全確定。

時間複雜度 \(O(n)\)

空間複雜度 \(O(n)\)

程式碼

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

vector<int> g[200007];
int cnt[200007];

int R[200007];
void dfs(int u) {
    R[u] = u;
    for (auto v : g[u]) {
        dfs(v);
        R[u] = max(R[u], R[v]);
    }
}

bool solve() {
    int n;
    ll w;
    cin >> n >> w;
    for (int i = 1;i <= n;i++) {
        cnt[i] = 0;
        g[i].clear();
    }
    for (int i = 2;i <= n;i++) {
        int p;
        cin >> p;
        g[p].push_back(i);
    }

    dfs(1);

    for (int i = 2;i <= n;i++) {
        cnt[i - 1]++;
        cnt[R[i]]++;
    }

    int rest = n;
    ll ans = n * w;
    for (int i = 1;i <= n - 1;i++) {
        int x;
        ll y;
        cin >> x >> y;
        cnt[x - 1]--;
        cnt[R[x]]--;
        w -= y;
        ans -= 1LL * (rest - 2) * y;
        if (cnt[x - 1] == 0) ans -= w, rest--;
        if (cnt[R[x]] == 0) ans -= w, rest--;
        cout << ans << ' ';
    }
    cout << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}