「CF995F」Cowmpany Cowmpensation

传送门

题目大意就是给你一颗有根树,每个点的权值可以是 ([1, D]) 的任意一个数,需要满足一个节点的权值不比它的父亲的大,求不同赋值情况的方案数。

首先我们可以考虑一个比较显然的 (O(nD)) 做法:

考虑 ( ext{DP}),设 (dp_{u, j}) 表示 (u) 的子树的方案,其中 (u) 的权值不超过 (j)

初始状态 (dp_{u, j} = 1),考虑每一颗 (u) 的子树 (v),我们每次都把 (dp_{u, j}) 乘上 (dp_{v, j})。然后这些方案是有包含关系的,所以还要前缀和一下。

那么我们现在就可以求出 (ans_i = dp_{1, i}, i in [1, n])

可以证明 (ans_i) 是一个关于 (i) 的不超过 (i + 1) 次的多项式。

那么我们就直接拉格朗日插值一下就好了。

参考代码:

#include <cstdio>

const int _ = 3002, p = 1e9 + 7;

int tot, head[_]; struct Edge { int v, nxt; } edge[_];
inline void Add_edge(int u, int v) { edge[++tot] = (Edge) { v, head[u] }, head[u] = tot; }

int n, k, dp[_][_], ans;

inline int power(int x, int k) {
    int s = 1;
    for (; k; k >>= 1, x = 1ll * x * x % p)
        if (k & 1) s = 1ll * s * x % p;
    return s % p;
}

inline void dfs(int u) {
    for (int i = 1; i <= n; ++i) dp[u][i] = 1;
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].v; dfs(v);
        for (int j = 1; j <= n; ++j)
            dp[u][j] = 1ll * dp[u][j] * dp[v][j] % p;
    }
    for (int i = 1; i <= n; ++i) dp[u][i] = (dp[u][i] + dp[u][i - 1]) % p;
}

inline int Lagrange() {
    int ans = 0;
    for (int i = 0; i <= n; ++i) {
        int s1 = dp[1][i], s2 = 1;
        for (int j = 0; j <= n; ++j)
            if (i != j) {
                s1 = 1ll * s1 * ((k - j + p) % p) % p;
                s2 = 1ll * s2 * ((i - j + p) % p) % p;
            }
        ans = (ans + 1ll * s1 * power(s2, p - 2)) % p;
    }
    return ans;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
#endif
    scanf("%d %d", &n, &k);
    for (int x, i = 2; i <= n; ++i) scanf("%d", &x), Add_edge(x, i);
    dfs(1);
    if (k <= n) printf("%d
", dp[1][k]);
    else printf("%d
", Lagrange());
    return 0;
}
原文地址:https://www.cnblogs.com/zsbzsb/p/13138273.html