BZOJ2111

推公式+Lucas定理

F[i]表示以i为根节点的堆的方案数

F[i] = F[2*i] * F[i*2+1] * C( d[i]-1 , d[2*i])

当n>=p的时候需要lucas定理

#include <iostream>
#include <cstdio>
#define N 5000050
 
using namespace std;
typedef long long LL;
LL d[N],F[N],jc[N],k; int n;
LL qp(LL a,LL b) {
    LL r = 1LL;
    while (b) {
        if (b&1) r = (r * a) % k;
        b >>= 1; a = (a * a) % k;
    }
    return r;
}
 
LL C(LL a,LL b) {
    if (b>a) return 0;
    if (b>=k || a>=k) return C(a/k , b/k) * C(a%k , b%k) % k;
    LL r = jc[a] * qp(jc[b],k-2) % k;
    r = r * qp(jc[a-b],k-2) % k;
    return r;
}
 
inline void solve() {
    cin >> n >> k; 
    jc[0] = 1LL;
    for (int i=1;i<=1000000;i++) jc[i] = jc[i-1] * i % k;
    for (int i=n;i>=1;i--) d[i] = d[2*i] + d[2*i+1] + 1;
    for (int i=n;i>=1;i--) {
    	LL debug = C(d[i]-1,d[2*i]);
    	F[i] = ( ( (2*i)<=n ? F[2*i] : 1 ) * ( (2*i+1)<=n ? F[2*i+1] : 1) % k ) * C(d[i]-1,d[2*i]) % k;
    }
       
    cout << F[1] << endl;
}
 
int main()
{
	
    solve();
    return 0; 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值