bzoj2111 [ZJOI2010]Perm 排列计数

本文介绍了一种使用递归和记忆化搜索的方法来计算具有特定数量节点的小根堆的不同结构总数。通过定义递推公式并利用Lucas定理解决组合数计算中涉及的大数问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

转化一下题意,求有n个节点的小根堆一共有几种。

那么,显然递推了。

f[x]=C(x-1,left)*f[x-1-left]*f[left]

left表示一个儿子的大小。
显然,记忆化好写很多。

要用Lucas

#include<bits/stdc++.h>
#define LL long long
#define MAXN 1000000
using namespace std;
LL fac[MAXN+1],n,p;
LL dp[MAXN+1];
LL ksm(LL A,LL B,LL C)
{
    LL Ans=1;
    while(B)
    {
        if(B&1)Ans=Ans*A%C;
        A=A*A%C;
        B>>=1;
    }
    return Ans;
}
LL C(LL N,LL M,LL P)
{
    return fac[N]*ksm(fac[M],P-2,P)%P*ksm(fac[N-M],P-2,P)%P;
}
LL Lucas(LL N,LL M,LL P)
{
    if(N==0||M==0)return 1;
    return Lucas(N/P,M/P,P)*C(N%P,M%P,P)%P;
}
LL f(LL x)
{
    if(dp[x])return dp[x];
    if(x==0)return dp[x]=1;
    if(x==1)return dp[x]=1;
    LL dep=log2(x);
    LL tot=((LL)1<<dep)-1;
    LL left=(tot-1)/2+min(x-tot,(tot+1)/2);
    return dp[x]=Lucas(x-1,left,p)*f(x-1-left)%p*f(left)%p;
}
int main()
{
    cin>>n>>p;
    fac[0]=1;
    for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%p;
    cout<<f(n);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值