洛谷 P2606 [ZJOI2010]排列计数 分析

分析

题意可以简化为用 $[1,n]$ 的数,组成一个完全二叉树,使其满足小根堆性质,求方案数。

令 $f_i$ 表示在 $i$ 点的方案数,$s_i$ 表示 $i$ 的子节点个数(包括 $i$),于是得出递推式:

$$
f_i=C^{s_i-1}_{s_{i \times 2}} \times f_{i*2} \times f_{i*2+1}
$$

由于 BZOJ 上 $n>m$(可能),所以需要用 Lucas 定理。

代码

namespace LZX
{
    using namespace std;
    #define int long long
    const int MAXN=2000015;
    int fac[MAXN],s[MAXN],f[MAXN];
    int math_qpow(int base,int power,int mod)
    {
        int res=1;
        while(power)
        {
            if(power&1)
            {
                res=res*base%mod;
            }
            base=base*base%mod;
            power>>=1;
        }
        return res;
    }
    int math_C(int n,int m,int p)
    {
        return fac[n]*math_qpow(fac[m]*fac[n-m]%p,p-2,p)%p;
    }
    int math_lucas(int n,int m,int p)
    {
        if(!m)
        {
            return 1;
        }
        if(m>n)
        {
            return 0;
        }
        return math_C(n%p,m%p,p)*math_lucas(n/p,m/p,p)%p;
    }
    int _main()
    {
        int n,m;
        scanf("%lld%lld",&n,&m);
        fac[0]=1;
        for(int i=1;i<=n;i++)
        {
            fac[i]=fac[i-1]*i%m;
        }
        fill(s+1,s+n+1,1);
        for(int i=n;i>=2;i--)
        {
            s[i>>1]+=s[i];
        }
        fill(f+n+1,f+n*2+2,1);
        for(int i=n;i;i--)
        {
            f[i]=math_lucas(s[i]-1,s[i<<1],m)%m*f[i<<1]%m*f[i*2+1]%m;
        }
        printf("%lld\n",f[1]);
        return 0;
    }
}

 

posted @ 2022-05-18 14:14  Day_Dreamer_D  阅读(38)  评论(0)    收藏  举报