树上启发式合并

树上启发式合并

作用

主要解决树上对每个根节点的影响,复杂度最好O(nlog n),最差O(N*N)
树上启发式合并

#include <bits/stdc++.h>

using namespace std;
#define ll long long
const ll N = 1e5 + 10;
ll dep[N], sze[N], son[N];
ll dian[N];
ll lev[N];
ll max_len;
ll ans[N];
ll flag;
ll he[2 * N], ne[2 * N], to[2 * N];
ll dex;
void add(ll x, ll y) {
    dex++;
    ne[dex] = he[x];
    he[x] = dex;
    to[dex] = y;
}//建边
struct node {
    ll l, r, sum;
} a[N << 4];
//线段树
void pushup(ll x) { a[x].sum = max(a[x << 1].sum, a[x << 1 | 1].sum); }
void build(ll x, ll l, ll r) {
    a[x].l = l, a[x].r = r;
    if (l == r) {
        a[x].sum = lev[l];
        return;
    }
    ll mid = (l + r) >> 1;
    build(x << 1, l, mid);
    build(x << 1 | 1, mid + 1, r);
    pushup(x);
}
void change(ll x, ll u, ll v) {
    if (a[x].l == u && a[x].r == u) {
        a[x].sum += v;
    } else {
        ll mid = a[x].l + a[x].r >> 1;
        if (u <= mid)
            change(x << 1, u, v);
        else
            change(x << 1 | 1, u, v);
        pushup(x);
    }
}
ll query(ll x, ll l, ll r) {
    if (a[x].l >= l && a[x].r <= r)
        return a[x].sum;
    else {
        ll v = 0;
        ll mid = a[x].l + a[x].r >> 1;
        if (l > mid)
            v = query(x << 1 | 1, l, r);
        else if (r <= mid)
            v = query(x << 1, l, r);
        else {
            v = query(x << 1 | 1, l, r);
            v = max(v, query(x << 1, l, r));
        }
        return v;
    }
}
//树链刨分 找重儿子
void dfs_son(int u, int fa) {
    sze[u] = 1, dep[u] = dep[fa] + 1;
    max_len = max(max_len, dep[u]);
    lev[dep[u]] += dian[u];
    ll maxn = 0;
    for (int i = he[u]; i; i = ne[i]) {
        int j = to[i];
        if (j == fa) continue;
        dfs_son(j, u);
        sze[u] += sze[j];
        if (sze[j] > maxn) {
            maxn = sze[j];
            son[u] = j;
        }
    }
}
//计算贡献
void count(int u, int f, int val) {
    change(1, dep[u], dian[u] * (-val));
    for (int i = he[u]; i; i = ne[i]) {
        int v = to[i];
        if (v == f || v == flag) continue;
        count(v, u, val);
    }
}
//树上启发式合并
void dfs(int u, int fa, bool keep) {
    for (int i = he[u]; i; i = ne[i]) {
        int j = to[i];
        if (j == fa || j == son[u]) continue;
        dfs(j, u, 0);
    }
    if (son[u]) {
        dfs(son[u], u, 1);
        flag = son[u];
    }
    count(u, fa, 1);
    ans[u] = query(1, 1, max_len);
    flag = 0;
    if (!keep) {
        count(u, fa, -1);
    }
}
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n - 1; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    for (int i = 1; i <= n; i++) cin >> dian[i];
    dfs_son(1, 0);
    build(1, 1, max_len);
    dfs(1, 0, 0);
    for (int i = 1; i <= n; i++) cout << ans[i] << " ";
}

树上启发式合并

上一篇:Kobe进入Hall of Fame‘s wifeSpeech


下一篇:Kuberneters CRD资源详解