Description
给出一棵 \(n\) 个点的树,每个点有点权。求所有联通块的权值 \(k\) 大和,对 \(64123\) 取模。
\(1\leq n,k\leq 1666\)
Solution
写正解是不可能的,这辈子都不可能的。只有写暴力才能维持的了生活这样子。
不妨枚举连通块内第 \(k\) 大的点。为了方便讨论,当点权相同时,点的标号越大,点优先级越高。
将优先级高于枚举的点的点权赋值为 \(1\) ,低于枚举的点的点权赋值为 \(0\) 。然后就是找包含枚举的点在内的连通块点权和 \(= k-1\) 的个数。
简易的树 \(dp\) 即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1666+5, yzh = 64123;
int n, k, w, d[N], u, v, root, ans, f[N][N];
struct tt {int to, next; }edge[N<<1];
int path[N], top;
void dfs(int u, int fa) {
if (d[u] > d[root] || (d[u] == d[root] && u > root)) {
f[u][1] = 0; for (int i = 1; i < k; i++) f[u][i+1] = f[fa][i];
}else for (int i = 1; i <= k; i++) f[u][i] = f[fa][i];
for (int i = path[u]; i; i = edge[i].next)
if (edge[i].to != fa) dfs(edge[i].to, u);
for (int i = 1; i <= k; i++) {
f[fa][i] += f[u][i];
if (f[fa][i] >= yzh) f[fa][i] -= yzh;
}
}
void add(int u, int v) {edge[++top] = (tt){v, path[u]}; path[u] = top; }
void work() {
scanf("%d%d%d", &n, &k, &w);
for (int i = 1; i <= n; i++) scanf("%d", &d[i]);
for (int i = 1; i < n; i++) {
scanf("%d%d", &u, &v); add(u, v), add(v, u);
}
for (int i = 1; i <= n; i++) {
root = i; int tot = 0;
for (int j = 1; j <= n; j++) tot += bool(d[j] > d[i] || (d[j] == d[i] && j > i));
if (tot < k-1) continue; f[i][1] = 1;
for (int j = 2; j <= k; j++) f[i][j] = 0;
for (int j = path[i]; j; j = edge[j].next) dfs(edge[j].to, i);
ans += 1ll*d[i]*f[i][k]%yzh; if (ans >= yzh) ans -= yzh;
}
printf("%d\n", ans);
}
int main() {work(); return 0; }