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 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”