之前提到过purfer编码了。
一棵树唯一对应一个purfer编码,一个purfer编码唯一对应一棵树。
1005: [HNOI2008]明明的烦恼
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 3664 Solved: 1459
[ 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
一棵n个节点的树一定对应一个n-2的purfer编码,而度数确定的点一定会出现在purfer编码内。
我们假设cnt为确定度数的点的个数,sum为所有确定的点的度数-1之和。
则,n-2里面必定要出现sum个确定的数,方案数为C(sum,n-2),并且这sum个数的每一种排列都是一种新的方案。
由不尽相异的全排列,方案数C(sum,n-2)*sum!/π(d[i]-1)!
剩下的n-2-sum个位置就排剩下的n-cnt个,每个位置随便填,就是(n-cnt)^(n-2-sum).
总方案数为确定度数填的方案*不确定度数填的方案。
ans=C(sum,n-2)*sum!/π(d[i]-1)! *(n-cnt)^(n-2-sum)
展开组合项化简,ans=(n-2)!/(π(d[i]-1)! *(n-2-sum)!) *(n-cnt)^(n-2-sum)
当n-2-sum<0的时候无解,当d[i]=0的时候无解,当n=1的时候讨论一下。
剩下就是高精度了
(PS:我的高精度把高精*低精先把低精转成高精再相乘,结果T了,改了之后过了)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define base 1000000
#define maxlen 510
#define get(x) (x-'0')
using namespace std;
const int maxn=1000+5;
/*
一共n个点,n-1条边,度数为2n-2
对于度数确定的,可以在kirchhoff矩阵直接在对角线上设,
不确定的,枚举。
然后把所有kirchhoff矩阵的主子式加起来
然而复杂度是难以接受的
所以正解是:http://www.cnblogs.com/zhj5chengfeng/archive/2013/08/23/3278557.html
考虑n个点一定对应n-2长度的purfer编码。
*/
int n;
int di[maxn];
int cnt;//对度数作要求个数
int sum;//确定度数之和
struct bign
{
int sign,len;
int c[maxlen];
bign()
{
sign=0;
len=1;
memset(c,0,sizeof(c));
}
void zero()
{
while(len-1&&!c[len])len--;
}
void writen(char *s)
{
int l=strlen(s),lim=0,k=1;
if(s[0]=='-')
{
sign=1;
lim=1;
}
for(int i=l-1;i>=lim;i--)
{
c[len]+=get(s[i])*k;
k*=10;
if(k==base)
{
k=1;
len++;
}
}
}
void Print()
{
if(sign)puts("-");
printf("%d",c[len]);
for(int i=len-1;i>=1;i--)printf("%06d",c[i]);
printf("\n");
}
bign operator =(int a)
{
while(a)
{
c[len++]=a%base;
a/=base;
}
zero();
return *this;
}
bool operator <(const bign &b)
{
if(len!=b.len)return len<b.len;
for(int i=len;i>=1;i--)
{
if(c[i]!=b.c[i])return c[i]<b.c[i];
}
return false;
}
bool operator >=(const bign &b)
{
if(len!=b.len)return len>b.len;
for(int i=len;i>=1;i--)
{
if(c[i]!=b.c[i])return c[i]>b.c[i];
}
return true;
}
bign operator +(const bign &b)
{
bign r;
r.len=max(len,b.len)+1;
for(int i=1;i<=r.len;i++)
{
r.c[i]=c[i]+b.c[i];
r.c[i+1]+=r.c[i]/base;
r.c[i]%=base;
}
r.zero();
return r;
}
bign operator +(const int &a)
{
bign b;
b=a;
return *this+b;
}
bign operator *(const bign &b)
{
bign r;
r.len=len+b.len+2;
for(int i=1;i<=len;i++)
{
for(int j=1;j<=b.len;j++)
{
r.c[i+j-1]+=c[i]*b.c[j];
r.c[i+j]+=r.c[i+j-1]/base;
r.c[i+j-1]%=base;
}
}
r.zero();
return r;
}
bign operator *(const int &a)
{
bign r;
r.len=len+5;
for(int i=1;i<=r.len;i++)
{
r.c[i]+=c[i]*a;
r.c[i+1]+=r.c[i]/base;
r.c[i]%=base;
}
r.zero();
return r;
}
bign operator -(const bign &b)
{
bign r=*this,y=b;
if(r<y)
{
swap(r,y);
r.sign=1;
}
for(int i=1;i<=r.len;i++)
{
r.c[i]-=y.c[i];
if(r.c[i]<0)
{
r.c[i]+=base;
r.c[i+1]--;
}
}
r.zero();
return r;
}
bign operator -(const int &a)
{
bign b;
b=a;
return *this-b;
}
bign operator /(const bign &b)
{
bign res,ans;
ans.len=len;
for(int i=len;i>=1;i--)
{
res=res*base;
res.c[1]=c[i];
while(res>=b)
{
ans.c[i]++;
res=res-b;
}
}
ans.zero();
return ans;
}
bign operator /(const int &a)
{
bign ans;
ans.len=len;
int res=0;
for(int i=len;i>=1;i--)
{
ans.c[i]=(res+c[i])/a;
res=(c[i]+res)%a*base;
}
ans.zero();
return ans;
}
bign operator ^(const int &y)
{
bign res;
bign r=*this;
res=1;
int t=y;
while(t)
{
if(t&1)res=res*r;
t>>=1;
r=r*r;
//if(t<100)r.Print();
}
return res;
}
}f;
int main()
{
cnt=0;
sum=0;
f=1;
scanf("%d",&n);
if(n==1)
{
int x;
scanf("%d",&x);
if(x==1)printf("1\n");
else printf("0\n");
return 0;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&di[i]);
if(di[i]==0)
{
printf("0\n");
return 0;
}
if(di[i]!=-1)
{
cnt++;
sum+=di[i]-1;
}
}
/*n-2的位置一定要选sum个位置 C(sum,n-2) 这sum位置进行
不尽相异全排列,得到 C(sum,n-2)*sum!/π(1~cnt)(di[i]-1)!
剩下n-2-sum个位置,排剩下的n-cnt个,随便排
(n-cnt)^(n-2-sum)*C(sum,n-2)*sum! /π(1~cnt)(di[i]-1)!
ans=(n-2)!*(n-cnt)^(n-2-sum)/(n-2-sum)!/连乘d[i]-1
*/
if(n-2-sum<0)
{
printf("0\n");
return 0;
}
for(int i=n-2-sum+1;i<=n-2;i++)
{
f=f*i;
}
for(int i=1;i<=n-2-sum;i++)f=f*(n-cnt);
for(int i=1;i<=n;i++)
{
if(di[i]>2)
{
for(int j=2;j<di[i];j++)f=f/j;
}
}
f.Print();
return 0;
}