【BZOJ】1005 明明的烦恼

Overview

【题意】给出标号为 1 N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?

【范围】 0<N1000

Analysis

题解1号

题解2号

上面的题解的推导已经很详细了,公式如下:

total=(n2)!×nn2cnt(n2cnt)!×ni=1(di1)!

先把特殊情况和非法情况处理掉。
对于 n=1 ,需要特判是否有边。
对于 n<2+cnt ,非法。

接着计算公式。
由于所有看得见的数值都是可直接计算的,所以考虑记录因数个数,然后再用高精度乘单精度。
先明确一点,由于我们最后的结果是整数,所以不论公式长什么样,一定有因数个数非负。

对于阶乘,可以预处理出每个数的情况,然后用前缀和;也可以不断整除取余来累加(小学希望杯培训……)

对于 nn2cnt ,直接求 n 的分解情况,然后乘上n2cnt即可。

Sumarize

高精度中分解质因数,将因数累加的使用:
①已知的数都可以直接计算
②阶乘可以直接用整除来分解,也可以使用前缀和
③对于小的数,最好用欧拉筛法等方法先求出所有的质因数来优化时间

Prufer编码:
构造与还原的方法;
②经常跟度数树的计数有关。

Code

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

const int N=1010;

int n,d[N];
int cnt[N],sum;
int v[N],p[N],c[N][N];
int rc[N],res[N];

inline int read(void)
{
    int s=0,f=1; char c=getchar();
    for (;c<'0'||c>'9';c=getchar()) if (c=='-') f=-1;
    for (;'0'<=c&&c<='9';c=getchar()) s=(s<<1)+(s<<3)+c-'0';
    return s*f;
}

int mutiply(int k)
{
    int m,g=0;
    for (int i=1;i<=res[0];i++)
    {
        m=res[i]*k+g;
        res[i]=m%10;
        g=m/10;
    }
    for (;g;g/=10) res[++res[0]]=g%10;
}

int main(void)
{   
    n=read();
    for (int i=1;i<=n;i++) d[i]=read();

    for (int i=1;i<=n;i++)
    {
        if (!d[i]) {printf("0\n");return 0;}
        if (d[i]>0) sum+=cnt[++cnt[0]]=d[i]-1;
    }

    if (n==1) {printf("%d\n",!cnt[0]?1:0);return 0;}
    if (n-2<sum) {printf("0\n");return 0;}

    for (int i=2;i<=n;i++)
    {
        if (!v[i]) p[++p[0]]=i;
        for (int j=1;j<=p[0];j++)
        {
            if (i*p[j]>n) break;
            v[i*p[j]]=1;
            if (i%p[j]==0) break;
        }
    }

    for (int i=1;i<=n;i++)
        for (int j=1;j<=p[0];j++)
            for (int k=i;k!=1&&k%p[j]==0;k/=p[j]) c[i][j]++;
    for (int i=1;i<=p[0];i++) rc[i]+=c[n-cnt[0]][i]*(n-2-sum);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=p[0];j++) c[i][j]+=c[i-1][j];
    for (int i=1;i<=p[0];i++) rc[i]+=c[n-2][i];
    for (int i=1;i<=p[0];i++) rc[i]-=c[n-2-sum][i];
    for (int i=1;i<=cnt[0];i++)
        for (int j=1;j<=p[0];j++) rc[j]-=c[cnt[i]][j];

    res[0]=res[1]=1;
    for (int i=1;i<=p[0];i++)
        for (int j=1;j<=rc[i];j++) mutiply(p[i]);
    for (int i=res[0];i;i--) printf("%d",res[i]);
    printf("\n");

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值