【prufer序列】【dp】神经元(neuron)

【描述】
你培育出了一些新型的神经元,它们可以有很多的轴突。

具体来说,对于第i个神经元,它有1di条轴突,因此可以与1di个神经元相连,可以将轴突看成无向图的边,假定每个神经元都是不同的。

现在你想知道,有多少种方案使得其中恰好k个神经元相连通,这里的连通需要保证任意两个神经元间有且仅有一条路径,方案数可能很大,你只需要对10^9+7取模输出。

两个方案是不同的当且仅当选择的神经元集合不同或其中有至少一条轴突(u,v)出现在一个方案但不出现在另一个方案。

【输入】
第1行包含一个整数n,表示神经元的个数。

第2行包含n个整数di,表示第i个神经元最多的轴突数量(1<=di<n)。

【输出】
输出1行,包含n个整数,第i个整数表示其中恰好有i个神经元连通的方案数模 10^9+7后的值。

【样例输入】
3
2 2 1
【样例输出】
3 3 2

【思路】

1.前置知识
prufer序列是生成树计数利器。简单介绍:
1.构造:每次删除树上度数为1的编号最小的点,并把该点当前所连的点的编号加进序列里,直到树上只有两个点。
2.性质:对于一个n个点的树,其prufer序列长度显然为n-2,且任何一个长度为n-2,元素大小在[1,n]的序列能还原出唯一一颗生成树。树上每个点在prufer序列里的出现次数为度数减1。

那么本题只需要利用上述性质即可完成。于是设计 d p [ i ] [ j ] [ k ] dp[i][j][k] 表示前i个点,有j个点在树上,只考虑这j个点的prufer序列的长度为k的方案数。转移时考虑把当前点插入树中即可,只需要考虑其在树上的度数,也就是考虑其在prufer序列中的出现次数。由于每个合法序列都对应一种方案,所以可以直接用插板法组合计数。时间复杂度 O ( n 4 ) O(n^4)

代码:

#include<bits/stdc++.h>
#define re register
using namespace std;
const int mod=1e9+7;
int n,a[101];
inline int red(){
    int data=0;bool w=0; char ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return w?-data:data;
}
int dp[101][101]={1};
int c[101][101];
inline int add(const int&a,const int&b){return (a+b>=mod)?a+b-mod:a+b;}
inline int mul(const int&a,const int&b){return 1ll*a*b%mod;}
int main(){n=red();
	for(int re i=1;i<=n;i++)a[i]=red()-1;
	for(int re i=0;i<101;i++)c[i][0]=1;
	for(int re i=1;i<101;i++)
		for(int re j=1;j<=i;++j)
			c[i][j]=add(c[i-1][j-1],c[i-1][j]);
	for(int re i=1;i<=n;i++)
		for(int re j=i;j;--j)
			for(int re k=0;k<=n-2;k++)
				for(int re l=min(k,a[i]);~l;--l)
					dp[j][k]=add(dp[j][k],mul(dp[j-1][k-l],c[k][l]));
	printf("%d",n); 
	for(int re i=2;i<=n;i++)printf(" %d",dp[i][i-2]);
}
发布了106 篇原创文章 · 获赞 22 · 访问量 5496

猜你喜欢

转载自blog.csdn.net/weixin_44111457/article/details/102017655