[九省联考 2018]秘密袭击coat

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; }
posted @ 2018-05-02 12:34  NaVi_Awson  阅读(241)  评论(0编辑  收藏  举报