您的位置:首页 > 其它

[HNOI2008] 明明的烦恼

2017-07-24 11:59 169 查看

[BZOJ1005][HNOI2008] 明明的烦恼

给出一棵树中所有节点的度数( −1 代表无限制),求可能的树的种类数。

0<N≤1000

嘛,这是我少有的单独为一道题写的博客,这水题卡了我一晚上,不过不能否认,这道题对现在的我来说确实很有价值,本体需要先推公式,然后分解质因数搞高精。

我们考虑用 pufer 编码来表示一棵树,根据 pufer 编码的性质:

任何一棵无根树都可以表示为长度为 n−2 的串;

一个点的度数是 d ,则它会在串中出现 d−1 次。

我们知道该已知度数的节点组成的串的总长度 len=∑i=1ndi−1 ,将长度为 len 的串中的数插入到总的串中,方法有 (lenn−2) 种,而长度为 len 的串共有 (lend1−1)(len−(d1−1)d2−1)…(dn−1dn−1) 种,再算上 m 个无度数限制的节点的 mn−2−len 种,根据乘法原理:

ans===(lenn−2)(lend1−1)(len−(d1−1)d2−1)…(dn−1dn−1)mn−2−len(n−2)!(n−2−len)!len!⋅len!(d1−1)!(len−d1−1)!…(dn−1)!(dn−1)!0!⋅mn−2−len(n−2)!⋅mn−2−len(n−2−len)!∏ni=1(di−1)!

由于答案十分巨大,需要写高精,不过高精除比较恶心,所以我们考虑分解质因数。

/**************************************************************
Problem: 1005
User: zhangche0526
Language: C++
Result: Accepted
Time:80 ms
Memory:1344 kb
****************************************************************/

#include<iostream>
#include<cstdio>

const int MAXN=1005;

int n;
int pIdx[MAXN],pri[MAXN],pcnt,dgr[MAXN];
bool notPri[MAXN];
void initPri(int r)
{
int i,j;
for(i=2;i<=r;i++)
{
if(!notPri[i]) pri[++pcnt]=i;
for(j=1;j<=pcnt&&pri[j]*i<=r;j++)
{
notPri[pri[j]*i]=true;
if(i%pri[j]==0) break;
}
}
}

void addIdx(int x,int idx)
{
for(int i=1;i<=pcnt&&x>1;i++)
while(x%pri[i]==0)
x/=pri[i],pIdx[i]+=idx;
}

int dig[MAXN*10],dL;
int main()
{
int i,j;
initPri(1000);
scanf("%d",&n);
if(n==1)
{
scanf("%d",&dgr[1]);
if(dgr[1]==0||dgr[1]==-1) printf("1\n");
else printf("0\n");
return 0;
}
if(n==2)
{
scanf("%d%d",&dgr[1],&dgr[2]);
if(dgr[1]==1&&dgr[2]==1) printf("1\n");
else printf("0\n");
return 0;
}
int dSum=0,limNcnt=0;bool noAns=false;
for(i=1;i<=n;i++)
{
scanf("%d",&dgr[i]);
if(dgr[i]==0||dgr[i]>=n) noAns=true;
if(dgr[i]==-1)
continue;
limNcnt++;
dSum+=dgr[i]-1;
for(j=1;j<=dgr[i]-1;j++)
addIdx(j,-1);
}
for(i=1;i<=n-2;i++) addIdx(i,1);
if(noAns){printf("0\n");return 0;}
for(i=1;i<=n-2-dSum;i++) addIdx(i,-1);
addIdx(n-limNcnt,n-2-dSum);
dL=dig[1]=1;
for(i=1;i<=pcnt;i++)
{
while(pIdx[i])
{
pIdx[i]--;
for(j=1;j<=dL;j++) dig[j]*=pri[i];
for(j=1;j<dL;j++) dig[j+1]+=dig[j]/10,dig[j]%=10;
while(dig[dL]>=10)
{
dig[dL+1]+=dig[dL]/10,dig[dL]%=10;
dL++;
}
}
}
for(i=dL;i;i--)
printf("%d",dig[i]);
printf("\n");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: