CF995F Cowmpany Cowmpensation

题目链接:http://codeforces.com/contest/995/problem/F

题目大意:

  给定一棵 \(n\) 个节点的有根树(根为 \(1\) 号结点),为这棵树上的每一个结点赋值(赋值的范围为 \([1,D]\)),要求父结点的值不小于子结点值,问有多少种赋值方案。

  \(1 \le n \le 3000, 1 \le D \le 10^9\)

知识点:  树形DP、拉格朗日插值法

解题思路:

  可以证明:对于一棵有 \(n\) 个结点的树,其答案是一个关于 \(D\) 的 \(n\) 次多项式 \(f(D)\)。(虽然并不知道怎么证明......)

  那么我们只需要用树形 \(DP\) 求出前 \(n+1\) 项:\(f(0), f(1), f(2), ... f(n)\),然后用拉格朗日插值法求出 \(f(D)\) 即可。

AC代码:

  1 #include <bits/stdc++.h>
  2
  3 using namespace std;
  4 typedef long long LL;
  5 const int MAXN=3005;
  6 const LL MOD = 1e9 + 7;
  7
  8
  9 LL powmod(LL x, LL y) {
 10     LL res = 1;
 11     x %= MOD;
 12     while (y) {
 13         if (y & 1)    res = res*x%MOD;
 14         y >>= 1;
 15         x = x*x%MOD;
 16     }
 17     return res;
 18 }
 19 namespace Polysum {
 20     //拉格朗日插值法对多项式求和
 21     const int D = 3010;
 22     //    LL a[D];  自改,待议
 23     LL f[D], g[D], p[D], p1[D], p2[D], b[D], h[D][2], C[D];
 24
 25     LL calcn(int d, LL *a, LL n) {
 26         if (n <= d)        return a[n];
 27         p1[0] = p2[0] = 1;
 28         for (int i = 0; i <= d; i++) {
 29             LL t = (n - i + MOD) % MOD;
 30             p1[i + 1] = p1[i] * t%MOD;
 31         }
 32         for (int i = 0; i <= d; i++) {
 33             LL t = (n - d + i + MOD) % MOD;
 34             p2[i + 1] = p2[i] * t%MOD;
 35         }
 36         LL ans = 0;
 37         for (int i = 0; i <= d; i++) {
 38             LL t = g[i] * g[d - i] % MOD*p1[i] % MOD*p2[d - i] % MOD*a[i] % MOD;
 39             if ((d - i) & 1)        ans = (ans - t + MOD) % MOD;
 40             else
 41                 ans = (ans + t) % MOD;
 42         }
 43         return ans;
 44     }
 45     void init(int M) {
 46         f[0] = f[1] = g[0] = g[1] = 1;
 47         for (int i = 2; i < M + 5; i++)
 48             f[i] = f[i - 1] * i%MOD;
 49         g[M + 4] = powmod(f[M + 4], MOD - 2);
 50         for (int i = M + 3; i >= 1;i--)
 51             g[i] = g[i + 1] * (i + 1) % MOD;
 52     }
 53     //对于给定的 x+1 个点,对应于它们的次数不超过 x 的拉格朗日多项式只有一个
 54     //因此,如果我们要求和的多项式的最高项是 m-1,那么我们就要输入其前 m 个点
 55     LL polysum(LL n, LL *a, int m) {
 56         //输入 a[] 的前 m 项:a[1], a[2], ..., a[m]
 57         //输出 a[] 的前 n 项之和: \sum_{i=0}^{n-1} a[i]
 58         //    LL b[D];  自改,待议
 59         for (int i = 0; i <= m; i++)    b[i] = a[i];
 60         b[m + 1] = calcn(m, b, m + 1);
 61         for (int i = 1; i < m + 2; i++)
 62             b[i] = (b[i - 1] + b[i]) % MOD;
 63         return calcn(m + 1, b, n - 1);
 64     }
 65     LL qpolysum(LL R, LL n, LL *a, int m) {
 66         //输入 a[] 的前 m+1 项:a[1], a[2], ..., a[m]
 67         //输出 a[x]*R^x 的前 n 项之和:sum_{i=0}^{n-1} a[i]*R^i
 68         if (R == 1)    return polysum(n, a, m);
 69         a[m + 1] = calcn(m, a, m + 1);
 70         LL r = powmod(R, MOD - 2);
 71         LL p3 = 0, p4 = 0, c, ans;
 72         h[0][0] = 0, h[0][1] = 1;
 73         for (int i = 1; i < m + 2;i++){
 74             h[i][0] = (h[i - 1][0] + a[i - 1])*r%MOD;
 75             h[i][1] = h[i - 1][1] * r%MOD;
 76         }
 77         for (int i = 0; i < m + 2; i++) {
 78             LL t = g[i] * g[m + 1 - i] % MOD;
 79             if (i & 1) p3 = ((p3 - h[i][0] * t) % MOD + MOD) % MOD, p4 = ((p4 - h[i][1] * t) % MOD + MOD) % MOD;
 80             else p3 = (p3 + h[i][0] * t) % MOD, p4 = (p4 + h[i][1] * t) % MOD;
 81         }
 82         c = powmod(p4, MOD - 2)*(MOD - p3) % MOD;
 83         for (int i = 0; i < m + 2; i++)
 84             h[i][0] = (h[i][0] + h[i][1] * c) % MOD;
 85         for (int i = 0; i < m + 2; i++)
 86             C[i] = h[i][0];
 87         ans = (calcn(m, C, n)*powmod(R, n) - c) % MOD;
 88         if (ans < 0)    ans += MOD;
 89         return ans;
 90     }
 91 }
 92
 93 vector<int> G[MAXN];
 94 LL dp[MAXN][MAXN],pre[MAXN][MAXN];
 95 int n;
 96 void dfs(int rt){
 97     bool flag=false;
 98     for(int i=0;i<G[rt].size();i++){
 99         int v=G[rt][i];
100         dfs(v);
101         flag=true;
102     }
103     for(int j=1;j<=n;j++)   dp[rt][j]=1;
104     if(flag){
105         for(int i=0;i<G[rt].size();i++){
106             int v=G[rt][i];
107             for(int j=1;j<=n;j++){
108                 dp[rt][j]=dp[rt][j]*pre[v][j]%MOD;
109             }
110         }
111     }
112     pre[rt][1]=dp[rt][1];
113     for(int j=2;j<=n;j++)
114         pre[rt][j]=(pre[rt][j-1]+dp[rt][j])%MOD;
115 }
116 int main(){
117     LL D;
118     scanf("%d%lld",&n,&D);
119     for(int i=2;i<=n;i++){
120         int t;
121         scanf("%d",&t);
122         G[t].push_back(i);
123     }
124     dfs(1);
125     Polysum::init(MAXN);
126     printf("%lld\n",Polysum::calcn(n,pre[1],D));
127
128     return 0;
129 }

原文地址:https://www.cnblogs.com/Blogggggg/p/9357301.html

时间: 2024-11-02 09:03:26

CF995F Cowmpany Cowmpensation的相关文章

[CF995F]Cowmpany Cowmpensation[树形dp+拉格朗日插值]

题意 给你一棵树,你要用不超过 \(D\) 的权值给每个节点赋值,保证一个点的权值不小于其子节点,问有多少种合法的方案. \(n\leq 3000, D\leq 10^9\) 分析 如果 \(D\) 比较小的话可以考虑状态 \(f_{i,j}\) 表示点 \(i\) 的权值是 \(j\) 的方案总数,\(g_{i,j}\) 表示 \(\sum_\limits{k=1}^jf_{i,j}\) ,转移也比较显然:\(f_{i,j}=\prod g_{son,j}\) 先证明结论:前?\(n\)?个正

CF995F Cowmpany Cowmpensation 动态规划+容斥原理

数数题还是要多练啊 code: #include <cstdio> #include <cstring> #include <string> #include <vector> #include <algorithm> #define N 3004 #define ll long long #define mod 1000000007 #define setIO(s) freopen(s".in","r",

Codeforces 995F Cowmpany Cowmpensation - 组合数学

题目传送门 传送点I 传送点II 传送点III 题目大意 给定一个棵$n$个点的有根树和整数$D$,给这$n$个点标号,要求每个节点的标号是正整数,且不超过父节点的标号,根节点的标号不得超过D. 很容易地能得到$O(nD)$的动态规划:设$f[i][j]$表示$i$号点标为$j$在它的子树内的方案数. 写写它的转移方程:$f[i][j] = \prod_{s \in Son(i)}\sum_{k = 1}^{j} f[s][k]$. 设$g[i][j]=\sum_{k = 1}^{j}f[i][