BZOJ1005: [HNOI2008]明明的烦恼
1005: [HNOI2008]明明的烦恼
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 5471 Solved: 2131
[Submit][Status][Discuss]
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在
任意两点间连线,可产生多少棵度数满足要求的树?
Input
第一行为N(0 < N < = 1000),
接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
Output
一个整数,表示不同的满足要求的树的个数,无解输出0
Sample Input
3
1
-1
-1
1
-1
-1
Sample Output
2
HINT
两棵树分别为1-2-3;1-3-2
Source
【题解】
http://www.cnblogs.com/zhj5chengfeng/archive/2013/08/23/3278557.html 很赞的博客
用到的序列性质:
1、对于度数是i的点,这个点在prufer中出现的次数是i - 1.
2、n个节点的树,Prufer序列长度为n - 2
3、任意一个Prufer序列均对应于一颗树
我们假设有m个不确定的节点
从n-2个空位中选出sum = Σd[i]-1个空位,对sum进行有重复全排列,于是
C(sum, n-2) * sum!/((d[1]-1)! * (d[1]-2)! * ..)
剩下的n - 2 - sum个位置,m个节点全随机选:m^(n - 2 - sum)
于是
ans = C(sum, n-2) * sum! * m^(n - 2 - sum)/((d[1]-1)! * (d[1]-2)! * ..)
= (n - 2)! * m^(n - 2 - sum)/(d[1]!*d[2]!*...*d[n]!*(n - 2 - sum)!)
然后暴力分解,避免高精除法
我因为高精乘脑抽多加一个=调了一个小时。。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 6 inline void read(int &x) 7 { 8 x = 0;char ch = getchar(),c = ch; 9 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 10 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 11 if(c == '-')x = -x; 12 } 13 14 const int MAXN = 1000 + 10; 15 16 int n, d[MAXN], m; 17 int k[MAXN]; 18 19 int ans[100000], len, sum; 20 21 void cheng(int a) 22 { 23 for(register int i = 1;i <= len;++ i)ans[i] *= a; 24 for(register int i = 1;i < len;++ i)ans[i + 1] += ans[i]/10, ans[i] %= 10; 25 for(;ans[len] >= 10;++ len)ans[len + 1] += ans[len]/10, ans[len] %= 10; 26 } 27 28 /* 29 30 C(n - 2 ,sum) * sum! * m^(n - 2 - sum)/(d[1]!*d[2]!*...*d[n]!) 31 = (n - 2)! * m^(n - 2 - sum)/(d[1]!*d[2]!*...*d[n]!*(n - 2 - sum)!) 32 */ 33 34 int main() 35 { 36 read(n); 37 register int tmp = 0; 38 for(register int i = 1;i <= n;++ i) 39 { 40 read(d[i]); 41 if(d[i] == 0) 42 { 43 printf("0"); 44 return 0; 45 } 46 if(d[i] == -1) 47 { 48 ++ m; 49 continue; 50 } 51 sum += d[i] - 1; 52 for(register int p = 2;p < d[i];++ p) 53 { 54 tmp = p; 55 for(register int j = 2;tmp > 1;++ j) 56 while(tmp % j == 0) -- k[j],tmp/= j; 57 } 58 } 59 if(n == 1 && d[1] != 0) 60 { 61 printf("0"); 62 return 0; 63 } 64 if(n == 2 && (d[1] != 1 || d[2] != 1)) 65 { 66 printf("0"); 67 return 0; 68 } 69 for(register int i = 2;i <= n - 2;++ i) 70 { 71 tmp = i; 72 for(register int j = 2;tmp > 1;++ j) 73 while(tmp % j == 0) ++k[j], tmp/= j; 74 } 75 for(register int i = 2;i <= n - 2 - sum;++ i) 76 { 77 tmp = i; 78 for(register int j = 2;tmp >1;++ j) 79 while(tmp % j == 0)-- k[j], tmp/= j; 80 } 81 tmp = m; 82 for(register int j = 2;tmp > 1;++ j) 83 while(tmp % j == 0)k[j] += n - 2 - sum, tmp/= j; 84 85 ans[1] = 1, len = 1; 86 87 for(int i = 1;i < MAXN;++ i) 88 for(;k[i] > 0;--k[i])cheng(i); 89 90 for(register int i = len;i >= 1;-- i)printf("%d", ans[i]); 91 return 0; 92 }