神经元(prufer序列+dp)
题目描述:
你培育出了一些新型的神经元,它们可以有很多的轴突。
具体来说,对于第i个神经元,它有1~di条轴突,因此可以与1~di个神经元相连,可以将轴突看成无向图的边,假定每个神经元都是不同的。
现在你想知道,有多少种方案使得其中恰好k个神经元相连通,这里的连通需要保证任意两个神经元间有且仅有一条路径,方案数可能很大,你只需要对10^9+7取模输出。
两个方案是不同的当且仅当选择的神经元集合不同或其中有至少一条轴突(u,v)出现在一个方案但不出现在另一个方案。
题解:
首先了解一下prufer序列:https://www.cnblogs.com/zwfymqz/p/8869956.html
由prufer序列的性质我们可以知道:
每个长度为n-2的prufer序列对应一棵n个节点的无根树。
因此,我们可以枚举符合条件的prufer序列个数。
定义 dp[i][j][k] 表示考虑到第i个数,用了j个数,prufer序列长度为k的序列个数,
dp时枚举下一个数选择几次即可。
1 dp[0][0][0]=1; 2 for(int i=0;i<n;i++){ 3 for(int j=0;j<=i;j++){ 4 for(int k=0;k<=n-2;k++){ 5 if(dp[i][j][k]){ 6 dp[i+1][j][k]+=dp[i][j][k];//此数不选 7 dp[i+1][j][k]%=MOD; 8 for(int cnt=0;cnt<=d[i+1]-1&&k+cnt<=n-2;cnt++){//枚举此数选几次 9 dp[i+1][j+1][k+cnt]+=dp[i][j][k]*c[k+cnt][k];//在k+cnt个位置中选k个按顺序放上一序列的数,其余位置放此数 10 dp[i+1][j+1][k+cnt]%=MOD; 11 } 12 } 13 } 14 } 15 }
所以,我们还要预处理一下c[][]数组(c[i][j]表示从i个数中选j个数的方案数),具体求法如下:
1 void init(){ 2 for(int i=0;i<=n;i++){ 3 c[i][0]=1; 4 for(int j=1;j<=i;j++){ 5 c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;//第i个数可选可不选 6 } 7 } 8 }
完整代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MOD=1e9+7; const int N=105; int n,d[N]; ll c[N][N],dp[N][N][N]; void init(){ for(int i=0;i<=n;i++){ c[i][0]=1; for(int j=1;j<=i;j++){ c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD; } } } int main(){ scanf("%d",&n); init(); for(int i=1;i<=n;i++){ scanf("%d",&d[i]); } dp[0][0][0]=1; for(int i=0;i<n;i++){ for(int j=0;j<=i;j++){ for(int k=0;k<=n-2;k++){ if(dp[i][j][k]){ dp[i+1][j][k]+=dp[i][j][k]; dp[i+1][j][k]%=MOD; for(int cnt=0;cnt<=d[i+1]-1&&k+cnt<=n-2;cnt++){ dp[i+1][j+1][k+cnt]+=dp[i][j][k]*c[k+cnt][k]; dp[i+1][j+1][k+cnt]%=MOD; } } } } } printf("%d ",n); for(int i=2;i<=n;i++){ printf("%lld ",dp[n][i][i-2]); } return 0; }