BZOJ2111 ZJOI2010排列计数

根据Pi>Pi/2可以看出来这是一个二叉树

所以我们可以用树形DP的思想

f[i]=f[i<<1]*f[i<<1|1]*C(s[i]-1,s[i<<1]),s是子树大小

然后求组合数可以用卢卡斯定理

BZ上加强数据后我那个线性求n!逆元就挂掉了,于是就直接算了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+5;
ll f[N<<1],fac[N],inv[N],s[N<<1];
ll n,mod;
ll qmod(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;b>>=1;
    }
    return ans;
}
ll C(ll a,ll b)
{
    if(a<b)return 0;
    if(a<mod&&b<mod)return fac[a]*qmod(fac[b]*fac[a-b]%mod,mod-2)%mod;
    return C(a/mod,b/mod)*C(a%mod,b%mod)%mod;
}
int main()
{
    scanf("%lld%lld",&n,&mod);
    fac[0]=1;
    for(int i=1;i<=n;++i)fac[i]=i*fac[i-1]%mod;
    for(int i=n;i;--i)
    {
        s[i]=s[i<<1]+s[(i<<1)|1]+1;f[i]=1;
        if((i<<1)<=n)f[i]=f[i]*f[i<<1]%mod;
        if((i<<1|1)<=n)f[i]=f[i]*f[i<<1|1]%mod;
        f[i]=f[i]*C(s[i]-1,s[i<<1])%mod;
    }
    printf("%lld",f[1]);
    return 0;
}

 

posted @ 2018-02-05 10:16  大奕哥&VANE  阅读(171)  评论(0编辑  收藏  举报