[ZJOI2010]排列计数 题解

Description

称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值

Input

输入文件的第一行包含两个整数 n和p,含义如上所述。

Output

输出文件中仅包含一个整数,表示计算1,2,⋯, ���的排列中, Magic排列的个数模 p的值。

Sample Input

20 23

Sample Output

16

HINT

100%的数据中,1 ≤ ��� N ≤ 106, P��� ≤ 10^9,p是一个质数。 数据有所加强

如图

把问题转化为

用1--n的数 组成一个完全二叉树使之满足小根堆性质的方案数

考虑dp

设i点的子结点数量为size[i]

则$dp[i]=C(s[i]-1,s[i*2])*f[i*2]*f[i*2+1]$

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
ll n,p;
ll dp[2000005],size[2000005],fac[1000005];
ll qpow(ll a,ll b,ll mod)
{
    ll res=1;
    a=a%mod;
    while(b)
    {
        if(b&1)res=(res*a)%mod;
        b=b>>1;
        a=(a*a)%mod;
    }
    return res;
}
ll C(ll x,ll y,ll mod)
{
    if(x<y)return 0;
    return fac[x]*qpow(fac[y],p-2,p)%p*qpow(fac[x-y],p-2,p)%p;
}
ll lucas(ll x,ll y,ll p)
{
    if(!y)return 1;
    return C(x%p,y%p,p)*lucas(x/p,y/p,p)%p;
}
int main()
{
    scanf("%lld%lld",&n,&p);
    fac[0]=fac[1]=1;
    for(int i=2;i<=n;i++)fac[i]=fac[i-1]*i%p;
    for(int i=n;i;i--)
    {
        size[i]=size[i<<1]+size[i<<1|1]+1;
        dp[i]=lucas(size[i]-1,size[i<<1],p);
        if(n>=(i<<1))dp[i]=dp[i]*dp[i<<1]%p;
        if(n>=(i<<1|1))dp[i]=dp[i]*dp[i<<1|1]%p;
    }
    //for(int i=1;i<=n;i++)cout<<dp[i]<<endl;
    cout<<dp[1]<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Rorschach-XR/p/11106878.html