bzoj 1005 [HNOI2008] 明明的烦恼 题解
来源:互联网 发布:淘宝店过户 编辑:程序博客网 时间:2024/04/28 17:38
转载请注明出处:http://blog.csdn.net/jiangshibiao/article/details/22651081
【原题】
1005: [HNOI2008]明明的烦恼
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 1963 Solved: 768
[Submit][Status]
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?
Input
第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
Output
一个整数,表示不同的满足要求的树的个数,无解输出0
Sample Input
1
-1
-1
Sample Output
HINT
两棵树分别为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;}
- bzoj 1005 [HNOI2008] 明明的烦恼 题解
- bzoj 1005: [HNOI2008]明明的烦恼
- bzoj 1005: [HNOI2008]明明的烦恼
- bzoj 1005: [HNOI2008]明明的烦恼
- 【Purfer】【bzoj 1005】: [HNOI2008]明明的烦恼
- BZOJ 1005 [HNOI2008]明明的烦恼
- BZOJ 1005 [HNOI2008]明明的烦恼
- BZOJ 1005 [HNOI2008]明明的烦恼
- BZOJ 1005 [HNOI2008] 明明的烦恼
- bzoj [1005] [HNOI2008]明明的烦恼
- bzoj 1005: [HNOI2008]明明的烦恼
- bzoj 1005: [HNOI2008]明明的烦恼
- bzoj 1005: [HNOI2008]明明的烦恼
- 1005: [HNOI2008]明明的烦恼
- 1005: [HNOI2008]明明的烦恼
- BZOJ 1005: [HNOI2008]明明的烦恼 Purfer数列
- 【BZOJ 1005】[HNOI2008]明明的烦恼 【Prufer序列】
- 【BZOJ 1005】[HNOI2008]明明的烦恼(暴力化简法)
- 【小算】
- Remoting技术
- i2c-dev模块--read/write支持DMA传输
- 堆排序
- SpringMVC与Freemarker整合获取request对象
- bzoj 1005 [HNOI2008] 明明的烦恼 题解
- 加密术相关历史典故
- android GridView视图的案例
- jfinal中model转map的方法实现
- 绿萝算法下网站站长如何建设外链
- 线程状态
- Android之LinearLayout学习
- 使用命令行模拟Eclipse中Android Project的编译流程(从源代码编译出最后的apk)
- HOJ 1191 (next_permutation()的应用)