纪中暑假集训 2020.07.31【NOIP提高组】模拟 T4:【GDSOI2017第二轮模拟】树

【GDSOI2017第二轮模拟】树

Description

有n个点,它们从1到n进行标号,第i个点的限制为度数不能超过A[i].
现在对于每个s (1 <= s <= n),问从这n个点中选出一些点组成大小为s的有标号无根树的方案数。

Input

第一行一个整数n.
第二行n个整数表示A[i].

Output

输出一行n个整数,第i个整数表示s=i时的答案。答案模1004535809 = 479 * 221+ 1。

Sample Input

3
2 2 1

Sample Output

3 3 2

Data Constraint

20%的数据:n <= 6
60%的数据:n <= 50
100%的数据:n <= 100

反思&题解

比赛&正解思路: 第一眼看到题,woc做过原题,这题白给了首先要想做这道题要知道一个知识点叫purfer序列,对于一棵大小为x的无根树,我们需要生成一个长度为 x 2 x-2 的purfer序列(当然大小为1的树答案就是n了),而每个节点的度数就是这个数在数列中出现的次数
f [ i ] [ j ] [ k ] f[i][j][k] 表示从前i个节点中取j个节点形成长度为k的purfer序列,转移方程很显然是
f [ i ] [ j ] [ k ] + = f [ i 1 ] [ j 1 ] [ k v ] C k v ( j > 0 , v < = m i n ( k , a [ i ] 1 ) f[i][j][k]+=f[i-1][j-1][k-v]*C_k^v (j>0,v<=min(k,a[i]-1)
注意注意一定要用杨辉三角与处理好组合数,如果在循环里面直接用组合数就会多带一个log爆掉(因为用费马小定理会有一个快速幂)我就是这样没了30……
反思: 曾经做过的题不切就不对了呀

CODE

#include<bits/stdc++.h>
using namespace std;
const long long mo=1004535809;
long long f[105][105][105],a[105],n,c[105][105]; 
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout); 
	scanf("%lld",&n);
	long long i,j;
	for (i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for (i=0;i<=n;i++)
		c[i][0]=1;
	for (i=1;i<=n;i++)
	{
		for (j=1;j<=n;j++)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
	}
	f[0][0][0]=1;
	long long k,v;
	for (i=1;i<=n;i++)
	{
		for (j=0;j<=i;j++)
		{
			for (k=0;k<=n-2;k++)
			{
				f[i][j][k]=f[i-1][j][k];
				if (j)
				{
					for (v=0;v<=min(a[i]-1,k);v++)
						f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k-v]*c[k][v])%mo;	
				}	
			}
		}
	}
	printf("%lld ",n);
	for (i=2;i<=n;i++)
		printf("%lld ",f[n][i][i-2]);
	printf("\n");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/CMC_YXY/article/details/107716070
今日推荐