分析
设 $dp_{i,j}$ 表示以 $i$ 为根的子树,$i$ 的权值为 $j$ 的方案数。容易得到转移
$$
dp_{i,j}=\prod_{v\in son_i}\sum_{k=1}^jdp_{v,k}
$$
前缀和优化即可做到 $\mathcal{O}(D)$ 转移,总时间复杂度 $\mathcal{O}(nD)$。
考虑优化。可以发现,$dp_{1,x}$ 为关于 $x$ 的 $n$ 次多项式(可以理解为每个叶子节点都为一次式,往上合并时做乘法从而次数上升)。
那么我们只需要求出 $dp_{1,1..n}$ 然后拉格朗日插值即可。时间复杂度 $\mathcal{O}(n^2)$。
代码
// ===================================
// author: M_sea
// website: https://m-sea-blog.com/
// ===================================
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
int X=0,w=1; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
return X*w;
}
const int N=3000+10;
const int mod=1e9+7;
int qpow(int a,int b) { int c=1;
for (;b;b>>=1,a=1ll*a*a%mod) if (b&1) c=1ll*c*a%mod;
return c;
}
int n,D,dp[N][N];
struct edge { int v,nxt; } e[N];
int head[N];
void addEdge(int u,int v) {
static int cnt=0;
e[++cnt]=(edge){v,head[u]},head[u]=cnt;
}
void dfs(int u) {
for (int i=1;i<=n;++i) dp[u][i]=1;
for (int i=head[u];i;i=e[i].nxt) {
int v=e[i].v; dfs(v);
for (int j=1;j<=n;++j)
dp[u][j]=1ll*dp[u][j]*dp[v][j]%mod;
}
for (int i=1;i<=n;++i) dp[u][i]=(dp[u][i]+dp[u][i-1])%mod;
}
int calc(int x) {
if (x<=n) return dp[1][x];
int s=1,res=0,o=n&1?mod-1:1;
for (int i=1;i<=n;++i) s=1ll*s*(x-i)%mod*qpow(i,mod-2)%mod;
for (int i=0;i<=n;++i,o=mod-o) {
res=(res+1ll*dp[1][i]*o%mod*s)%mod;
s=1ll*s*(x-i)%mod*qpow(x-i-1,mod-2)%mod*qpow(i+1,mod-2)%mod*(n-i)%mod;
}
return res;
}
int main() {
n=read(),D=read();
for (int i=2;i<=n;++i) addEdge(read(),i);
dfs(1); printf("%d\n",calc(D));
return 0;
}
3 条评论
tql
%%%插值大佬
还有我不看翻译看不懂啊qwq
您在fAKe吧