又是一道经典的暴力碾标算。
对每个点分别计算贡献最后加一块就好了。
假如当前正在计算s的贡献,设f[i][j]代表包含从s到i的路径且使得s为第j大的连通块个数,进行简单的树上dp,s最终带来的贡献就是f[s][k] * d[i]。
具体实现见代码。
#include <iostream>
#include <cstring>
#include <cstdio>
const int maxn = 2018;
const int mod = 64123;
using namespace std;
int n, k, w, now, ans;
int d[maxn];
int f[maxn][maxn];
int to[maxn << 1];
int nex[maxn << 1];
int last[maxn], cnt;
inline int read()
{
int X = 0; char ch = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') X = (X << 3) + (X << 1) + ch - '0', ch = getchar();
return X;
}
inline void add_edge(int x, int y)
{
to[++cnt] = y; nex[cnt] = last[x]; last[x] = cnt;
}
void dfs(int x, int fa)
{
if (d[now] < d[x] || (d[now] == d[x] && x < now))
for (int i = 1; i < k; i++) f[x][i+1] = f[fa][i];
else for (int i = 1; i <= k; i++) f[x][i] = f[fa][i];
for (int i = last[x]; i; i = nex[i])
if (to[i] != fa) dfs(to[i], x);
for (int i = 1; i <= k; i++) f[fa][i] = (f[fa][i] + f[x][i]) % mod;
}
int main(void)
{
n = read(), k = read(), w = read();
for (int i = 1; i <= n; i++) d[i] = read();
for (int i = 1; i < n; i++) {
int x = read(), y = read();
add_edge(x, y);
add_edge(y, x);
}
for (int i = 1; i <= n; i++) {
now = i;
int tot = 0;
for (int j = 1; j <= n; j++) if (d[j] > d[i] || (d[j] == d[i] && i > j)) tot++;
if (tot < k - 1) continue;
memset(f, 0, sizeof f);
f[i][1] = 1;
for (int j = last[i]; j; j = nex[j]) dfs(to[j], i);
ans = (ans + 1ll * f[i][k] * d[i]) % mod;
}
cout << ans;
return 0;
}