您的位置:首页 > 其它

bzoj1211树的计数

2016-03-06 19:38 281 查看
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1211

给定点的度数,判断能构成的树的个数。

没什么特别的,知道了purfer编码之后,一如既往的做就是了。

令sum=∑d[i]-1

考虑n-2长度的purfer码,总共会组成C(sum,n-2)*sum!/π(d[i]-1)! 种方案,(n-2个里面选sum个组合,同时这sum个元素会组成的不尽相异的全排列为sum!/π(d[i]-1)!)

而对于这道题,每个点的度数都给定了,如果存在解,一定会有sum=n-2  (一棵树的总的度数为2*n-2,每个点度数-1之和就是sum(purfer编码的长度),即n-2=sum)

还要特判d[i]==0的时候和n==1的时候

注意下特判,就可以做了。

PS:我用的高精度,网上貌似很多人用long long分解质因数,不过我亲测极限数据不能过(n=150,148个2和2个1,答案很会大,但是那些版本虽然快但是这组数据测不出来)

高精度虽然慢,但是很准确。亲测很多版本答案是0或1,而正确答案是很长一串(500多位吧)148!

比较慢,但是应该是相当准确的:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define base 1000000
#define maxlen 101
#define get(x) (x-'0')
using namespace std;
/*
n个点的树唯一对应一个n-2长度的purfewr编码,
sum!/π(d[i]-1)! sum=n-2
*/
const int maxn=170;
int di[maxn];
int n;
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=1,k=1;
if(s[0]=='-')
{
lim=1;
sign=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;
}
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 r;
r.len=len+10;
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;
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+10;
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 int &a)
{
bign ans;
int res=0;
for(int i=1;i<=len;i++)
{
ans.c[i]=(res+c[i])/a;
res=(res+c[i])%a*base;
}
ans.zero();
return ans;
}
}f;
int main()
{
scanf("%d",&n);
int sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&di[i]);
sum+=di[i];
if(di[i]<=0&&n>1)
{
printf("0\n");
return 0;
}
}
if(sum!=2*n-2)
{
printf("0\n");
return 0;
}
if(n==1)
{
printf("1\n");
return 0;
}
f=1;
for(int i=n-2;i>=2;i--)f=f*i;
for(int i=1;i<=n;i++)
{
if(di[i]>2)
{
for(int j=di[i]-1;j>=2;j--)
{
f=f/j;
}
}
}
f.Print();
return 0;
}

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  图上计数 bzoj 数学