题目描述

给定一个 个点的树,树有点权。

如果 的最近公共祖先(LCA),并且 的树上距离等于 ,那么点 的答案就会加上

求所有点的答案。

正解

考虑暴力 dp,设 表示 的子树内离 的距离为 的点的个数,设 表示 的子树内离 的距离为 的点权和。

每次合并一颗子树,更新答案并且更新 dp 数组,时间复杂度

发现 dp 复杂度只跟深度有关,那么长链剖分后可以优化复杂度至

其实还有 dsu 的做法,每次暴力合并复杂度也没有问题,是 的。

这里给出长链剖分的代码 :

#include <bits/stdc++.h>
#define N 100005

using namespace std;

int n, k;
int o[N], d[N], a[N], totVec;
int fa[N], wson[N], dist[N];
int head[N], nex[N << 1], to[N << 1], ecnt;

long long ans[N];
vector<long long> f[N], g[N];


inline int read() {
    int x = 0; char ch = getchar();
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x;
}
inline void addE(int u, int v) {
    to[++ecnt] = v;
    nex[ecnt] = head[u], head[u] = ecnt;
}

void preDfs(int u) {
    for(int i = head[u], v; i; i = nex[i]) {
        v = to[i];
        if(v == fa[u]) continue;
        fa[v] = u, preDfs(v);
        if(dist[wson[u]] < dist[v])
            wson[u] = v;
    }
    dist[u] = dist[wson[u]] + 1;
}

void dfs(int u, int top) {
    if(u == top) {
        o[u] = ++totVec;
        f[o[u]].resize(dist[u]);
        g[o[u]].resize(dist[u]);
    }
    if(wson[u]) {
        o[wson[u]] = o[u];
        d[wson[u]] = d[u] + 1;
        dfs(wson[u], top);
    }

    ++f[o[u]][d[u]];
    g[o[u]][d[u]] += a[u];
    for(int i = head[u], v; i; i = nex[i]) {
        v = to[i];
        if(v == fa[u] || v == wson[u]) continue;
        dfs(v, v);
        for(int j = 0; j < dist[v]; ++j)
            if(k - j - 1 > 0 && k - j - 1 + d[u] < dist[top]) {
                ans[u] += f[o[u]][k - j - 1 + d[u]] * g[o[v]][j];
                ans[u] += g[o[u]][k - j - 1 + d[u]] * f[o[v]][j];
            }
        for(int j = 0; j < dist[v]; ++j) {
            f[o[u]][j + 1 + d[u]] += f[o[v]][j];
            g[o[u]][j + 1 + d[u]] += g[o[v]][j];
        }
    }
}

int main() {
    n = read(), k = read();
    for(int i = 1; i <= n; ++i)
        a[i] = read();
    for(int i = 1, u, v; i < n; ++i) {
        u = read(), v = read();
        addE(u, v), addE(v, u);
    }

    preDfs(1);
    dfs(1, 1);

    for(int i = 1; i <= n; ++i)
        printf("%lld%c", ans[i], " \n"[i == n]);
    return 0;
}