【bzoj1005】【hnoi2008】明明的烦恼
Submit: 6660 Solved: 2624
[Submit][Status][Discuss]
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
Source
题意:求一种有编号的树的方案,这棵树有些点有度数限制,有些点没有;
题解:
首先要知道prufer序列(推荐:http://www.cnblogs.com/zhj5chengfeng/p/3278557.html)
(prufer的u上面好像还有两个点来着,懒得打了)
1.构造prufer:
每次选一个编号最小的叶子结点删除,同时把和它相连的那个点加入prufer序列,更新原图的度数,直到剩下两个点;
这可以用一个set来实现;
这样n个点的树的prufer长度为n-2,同时一个点的度数=在prufer序列里出现的次数+1;
2.由prufer序列还原树:
由上面的结论可以还原出点的度数,考虑prufer序列的每个点是如何被加进来的,维护最小叶节点就可以按照生成的方式还原树;
每一个树对应一个唯一的prufer,反过来也是;
所以我们说长度为n-2 的prufer序列和编号为1-n的树一一对应;
3.所以编号为1-n的数的个数为$n^{n-2}$ ;
加上度数的限制呢,假设每个点度数为di: $\frac{n^{n-2}}{\Pi (d_i – 1) }$
如果是像题目,仅有一些点有度数限制,考虑先排好有限制的点,选其他位置:(记有度数限制的点有$cnt$个,同时$sum=\sum_{i=1}^{cnt}(d_i - 1)$ )
$$C_{n-2}^{sum} \frac{sum!}{ \Pi_{i=1}^{cnt} {(d_i – 1)} } (n-cnt)^{n-2-sum}$$
$$\frac{ (n-2)! } { (n-2-sum)! \Pi{ (d_i – 1)! } } (n-cnt)^{n-sum-2} $$
需要高精度,但是可以用勒让德定理质因数分解代替中间部分的高精度,最后统一写个高精乘法。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<queue> 6 #include<cmath> 7 #include<vector> 8 #include<stack> 9 #include<map> 10 #define Run(i,l,r) for(int i=l;i<=r;i++) 11 #define Don(i,l,r) for(int i=l;i>=r;i--) 12 #define ll long long 13 #define inf 0x3f3f3f3f 14 using namespace std; 15 const int N = 1010,base = 10000; 16 int n,sum,cnt,pri[N],now[N],vis[N],sz,d[N]; 17 void pre(){ 18 for(int i=2;i<=1e3;i++){ 19 if(!vis[i])pri[++sz] = i; 20 for(int j=1;j<=sz&&i*pri[j]<=1e3;j++){ 21 vis[i*pri[j]] = 1; 22 if(i % pri[j]==0)break; 23 } 24 } 25 } 26 void get_num(int x,int y){ 27 if(!x)return; 28 int tmp = x; 29 for(int i=1;i<=sz;i++){ 30 while(tmp%pri[i]==0&&(tmp/=pri[i]))now[i]+=y; 31 } 32 } 33 void get_fac(int x,int y){ 34 for(int i=1;i<=sz;i++){ 35 int tmp = x; 36 while(tmp){ 37 now[i]+= tmp/pri[i] * y; 38 tmp/=pri[i]; 39 } 40 } 41 } 42 struct Bign{ 43 int len,c[1000]; 44 Bign(){len=0;memset(c,0,sizeof(c));} 45 void zero(){while(len&&!c[len])len--;} 46 void print(){ 47 zero(); 48 printf("%d",c[len]); 49 Don(i,len-1,0)printf("%04d",c[i]); 50 puts(""); 51 } 52 Bign operator = (const int&A){ 53 len=0; c[0]=A; 54 return *this; 55 } 56 Bign operator * (const int&A){ 57 Bign ret; 58 ret.len = len + 1; 59 for(int i=0;i<=len;i++){ 60 ret.c[i] += c[i] * A; 61 } 62 for(int i=0;i<=ret.len;i++){ 63 if(ret.c[i]>=base){ 64 ret.c[i+1] += ret.c[i] / base; 65 ret.c[i] %= base; 66 } 67 } 68 ret.zero(); 69 return ret; 70 } 71 Bign operator * (const Bign&A){ 72 Bign ret; 73 ret.len = len + A.len + 1; 74 for(int i=0;i<=len;i++) 75 for(int j=0;j<=A.len;j++){ 76 ret.c[i+j] += c[i] * A.c[j]; 77 if(ret.c[i+j]>=base){ 78 ret.c[i+j+1] += ret.c[i+j] / base; 79 ret.c[i+j] %= base; 80 } 81 } 82 for(int i=0;i<=ret.len;i++){ 83 if(ret.c[i]>=base){ 84 ret.c[i+1] += ret.c[i] / base; 85 ret.c[i] %= base; 86 } 87 } 88 ret.zero(); 89 return ret; 90 } 91 }; 92 int main(){ 93 // freopen("bzoj1005.in","r",stdin); 94 // freopen("bzoj1005.out","w",stdout); 95 pre(); 96 scanf("%d",&n); 97 for(int i=1;i<=n;i++){ 98 scanf("%d",&d[i]); 99 if(~d[i]){ 100 cnt++; 101 sum+=(d[i]-1); 102 } 103 } 104 if(sum>n-2){puts("0");return 0;} 105 get_fac(n-2,1); 106 get_fac(n-2-sum,-1); 107 for(int i=1;i<=n;i++)if(~d[i]){ 108 get_fac(d[i] - 1 , -1); 109 } 110 get_num(n - cnt , n - 2 - sum); 111 Bign ans; 112 ans = 1; 113 for(int i=1;i<=sz;i++)if(now[i]){ 114 Bign tmp; tmp = 1; 115 for(int j=1;j<=now[i];j++)tmp = tmp * pri[i]; 116 //tmp.print(); 117 ans = ans * tmp; 118 } 119 ans.print(); 120 return 0; 121 }//by tkys_Austin;