kruskal重构树

#


# ICPC2021上海H_LifeIsAGame

# 🔗

20220831220018

# 💡

为了尽可能多拿有价值的点,走的路径权值肯定是希望比较小的
这就和最小生成树很像了
那么快速得到从 xx 能走到的点,我们想知道从 xx 出发的最后能走到的边界,而在生成树上这样考虑试着转化成重构树
发现 xx 在原图走可以转化为 xx 往上跳,每跳一步就可以收集另一部分的点权变成子树权值和
树上往上跳能跳的最大步数可以用倍增去解决,倍增维护区间左端点跳到右端点所付出代价(-(边权-子树权值和))的最大值,当给出的 kk 满足不了时,就从能跳到的右边界继续向上跳,从而用二进制得到一个精确的能跳到的位置

#

const int N = 3e5 + 10;
const int M = 1e6 + 10;
struct Dsu {
    vector<int> f;
    inline Dsu (int n) {
        f.resize(n + 1);
        for (int i = 0; i <= n; i ++) f[i] = i;
    }
    inline int find (int x) {return x == f[x] ? x : f[x] = find(f[x]);}
    inline void merge (int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) return;
        f[x] = y;
    }
    inline bool check (int x, int y) {
        x = find(x);
        y = find(y);
        return x == y;
    }
};
int n, m, q;
int a[N];
struct node {
    int u, v, w;
    inline friend bool operator < (node a, node b) {
        return a.w < b.w;
    }
};

int w[N];
int fa[N][40];
int ct[N][40];

inline void Solve (int x, int k) {
    while (x != 2 * n - 1) {
        int t = x;
        for (int j = 20; j >= 0; j --) {
            if (ct[x][j] + k >= 0) {
                x = fa[x][j];
                break;
            }
        }
        if (x == t) break;
    }
    cout << a[x] + k << endl;
}
signed main () {
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    vector<node> v(m);
    for (node &it : v) cin >> it.u >> it.v >> it.w;

    int pt_idx = n;
    Dsu dsu(2 * n);
    sort(v.begin(), v.end());
    for (node it : v) {
        int itu = dsu.find(it.u);
        int itv = dsu.find(it.v);
        if (itu == itv) continue;
        w[++pt_idx] = it.w;
        dsu.merge(itu, pt_idx); dsu.merge(itv, pt_idx);
        fa[itu][0] = pt_idx;
        fa[itv][0] = pt_idx;
        a[pt_idx] += a[itu] + a[itv];
    } w[0] = 2e9 + 10;

    for (int i = 1; i <= 2 * n; i ++) 
        ct[i][0] = a[i] - w[fa[i][0]];
    for (int j = 1; j <= 20; j ++) {
        for (int i = 1; i <= 2 * n; i ++) {
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
            ct[i][j] = min(ct[i][j - 1], ct[fa[i][j - 1]][j - 1]);
        }
    } 

    while (q --) {
        int x, k; cin >> x >> k;
        Solve(x, k);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

Last Updated: 10/14/2023, 7:51:49 PM