自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?
转载请注明出处:http://blog.csdn.net/jiangshibiao/article/details/22651081
【原题】
自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?
第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
一个整数,表示不同的满足要求的树的个数,无解输出0
两棵树分别为1-2-3;1-3-2
【分析】开始觉得无从下手啊。在SYC1999和iGzhou的指导下,先了解了一种神奇的东西,叫做:prufer序列(无奈有这么多的公式、定理,以前怎么没有听说过呢)。
介绍一下:
一种生成Prufer序列的方法是迭代删点,直到原图仅剩两个点。对于一棵顶点已经经过编号的树T,顶点的编号为{1,2,...,n},在第i步时,移去所有叶子节点(度为1的顶点)中标号最小的顶点和相连的边,并把与它相邻的点的编号加入Prufer序列中,重复以上步骤直到原图仅剩2个顶点。以右边的树为例子,首先在所有叶子节点中编号最小的点是2,和它相邻的点的编号是3,将3加入序列并删除编号为2的点。接下来删除的点是4,5被加入序列,然后删除5,1,此时原图仅剩两个点,Prufer序列构建完成,为{3,5,1,3}
这其实就是对于一棵树的编码罢了。这有什么用呢?有一个定理是这样的:prufer编码是可以和无根树之间形成一一对应关系。这样子,计算树的个数,就转化成了计算序列的个数了。注意:题目里每个点的要求个数要减1后计算排列的个数,因为会有父节点。
【思考】怎么计算呢?就用排列组合的原理。我们先考虑不是-1的点。一共有N-2个格子,设当前的点要求为a1(已经减1了),我们可以放的总数是:C(N-2,a1)。这样占了a1个格子。下一个a2的情况就是C(N-2-a1,a2)。依次类推。
然后是-1的情况,我想了很长时间。
原来是这样想的:设最后还有K个格子,且有SUM个-1。我们用DP思路。f[i][j]表示放到最后K个格子中的第i个格子时,放第j种颜色(注意颜色是不下降的,最后只需乘上阶乘即可)的情况数。但是因为要用高精,DP会很麻烦。
感谢HHD大牛的指导:其实就是SUM^K。为什么?对于每一个格子,我们都可以放SUM种情况~~~~。
【优化】为了不超时,用质数表来优化。
【代码】
#include<cstdio> using namespace std; const int size=168; const int data[size+1]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73, 79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191, 193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311, 313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439, 443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577, 587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709, 719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857 ,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997}; int n,len,i,a,sum,j,k; int t[10001],ans[size+1]; void C(int n,int m) { int i,p,j; for (i=m+1;i<=n;i++) { p=i;j=1; while (p>1){while (p%data[j]==0) {ans[j]++;p/=data[j];}j++;} } for (i=1;i<=n-m;i++) { p=i;j=1; while (p>1){while (p%data[j]==0) {ans[j]--;p/=data[j];}j++;} } } int main() { scanf("%d",&n);len=n-2; for (i=1;i<=n;i++) { scanf("%d",&a); if (a==0||len-(a-1)<0) {printf("0");return 0;} if (a==-1) {sum++;continue;}a--; C(len,a);len-=a; } if (len) { j=1; while (sum>1){while (sum%data[j]==0) {ans[j]+=len;sum/=data[j];} j++;} } t[1]=1;k=1; for (i=1;i<=size;i++) while (ans[i]) { ans[i]--; for (j=1;j<=k;j++) t[j]*=data[i]; for (j=1;j<=k;j++) if (t[j]>9) {t[j+1]+=t[j]/10;t[j]%=10;} while (t[k+1]) {k++;t[k+1]+=t[k]/10;t[k]%=10;} } for (i=k;i>0;i--)printf("%d",t[i]); return 0; }