九省联考2018D1T3 秘密袭击

链接

又是一道经典的暴力碾标算。

对每个点分别计算贡献最后加一块就好了。

假如当前正在计算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;
}


猜你喜欢

转载自blog.csdn.net/star_city_7/article/details/79952639