【BZOJ1089】[SCOI2003]严格n元树(高精度,动态规划)

本文介绍了一种使用高精度动态规划算法解决严格n元树计数问题的方法。通过定义状态f[i]表示深度至多为i的n元树个数,利用递推公式f[i]=f[i-1]^n+1进行计算,并最终通过容斥原理得到深度恰好为d的严格n元树的数量。
摘要由CSDN通过智能技术生成

【BZOJ1089】[SCOI2003]严格n元树(高精度,动态规划)

题面

BZOJ
洛谷

题解

\(f[i]\)表示深度为\(i\)\(n\)元树个数。然后我们每次加入一个根节点,然后枚举它的子树的深度乘起来就好了。但是这样不好做,我们设\(f[i]\)表示深度至多为\(i\)\(n\)元树个数,那么显然,\(f[i]=f[i-1]^n+1\),加一的原因是存在只有一个根节点的情况。最终的答案直接容斥一下就变成了\(f[d]-f[d-1]\)。写个高精度就好了,反正位数不多,乘法直接暴力就行。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,d;
struct BigInt
{
    int s[2000],ws;
    void init(){memset(s,0,sizeof(s));s[ws=1]=0;}
    void output(){for(int i=ws;i;--i)printf("%d",s[i]);puts("");}
}f[20],One;
BigInt operator+(BigInt a,BigInt b)
{
    int ws=max(a.ws,b.ws);
    for(int i=1;i<=ws;++i)a.s[i]+=b.s[i];
    for(int i=1;i<=ws;++i)a.s[i+1]+=a.s[i]/10,a.s[i]%=10;
    while(a.s[ws+1])++ws,a.s[ws+1]+=a.s[ws]/10,a.s[ws]%=10;
    a.ws=ws;return a;
}
BigInt operator-(BigInt a,BigInt b)
{
    int ws=a.ws;
    for(int i=1;i<=b.ws;++i)a.s[i]-=b.s[i];
    for(int i=1;i<=ws;++i)if(a.s[i]<0)a.s[i]+=10,a.s[i+1]-=1;
    while(!a.s[ws])--ws;
    a.ws=ws;return a;
}
BigInt operator*(BigInt a,BigInt b)
{
    BigInt ret;int ws=a.ws+b.ws;ret.init();
    for(int i=1;i<=a.ws;++i)
        for(int j=1;j<=b.ws;++j)
            ret.s[i+j-1]+=a.s[i]*b.s[j];
    for(int i=1;i<=ws;++i)ret.s[i+1]+=ret.s[i]/10,ret.s[i]%=10;
    while(!ret.s[ws])--ws;
    ret.ws=ws;return ret;
}
BigInt fpow(BigInt a,int b)
{
    BigInt s;s.init();s.s[1]=1;
    while(b){if(b&1)s=s*a;a=a*a;b>>=1;}
    return s;
}
int main()
{
    cin>>n>>d;f[0].init();f[0].s[1]=1;One.init();One.s[1]=1;
    for(int i=1;i<=d;++i)f[i]=fpow(f[i-1],n)+One;
    f[d]=f[d]-f[d-1];f[d].output();
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/9739114.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值