bzoj 2111 [ZJOI2010]Perm 排列计数

2111: [ZJOI2010]Perm 排列计数

Time Limit: 10 Sec  Memory Limit: 259 MB

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,⋯, n的排列中, Magic排列的个数模 p的值。

Sample Input

20 23

Sample Output

16

HINT

 

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

 

Source

 

Tips:

  数据有所加强,因为N比较小,可以用lucas定理

  树形dp

  dp[i]=dp[i*2]*dp[i*2+1]*C(size[i]-1,size[i*2])%P;

 

Code:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
long long n,m,p,size[4000008],dp[4000008],f[4000008];

long long ksm(long long a,long long b,long long MOD){
    long long res=1;
    while(b>0){
        if(b%2==1) res=a*res%MOD;
        b=b/2;
        a=a*a%MOD;
    }
    return res;
}

void init(long long MOD){
    f[0]=1;
    for(int i=1;i<=n;i++)
        f[i]=f[i-1]*i%MOD;
}

long long lucas(long long a,long long b,long long MOD){
    long long res=1;
    while(a&&b){
        long long aa=a%MOD,bb=b%MOD;
        res=res*f[aa]*ksm(f[aa-bb]*f[bb]%MOD,MOD-2,MOD)%MOD;
        a=a/MOD; b=b/MOD;
    }
    return res;
}

int main(){
    scanf("%lld%lld",&n,&p);
    for(int i=1;i<=2*n;i++)
        dp[i]=1;
    init(p);
    for(int i=n;i>0;i--){
        size[i]++;
        size[i/2]+=size[i];
    }
    
    for(int i=n;i>0;i--){
        if(size[i]==1) continue;
        else{
            dp[i]=dp[i*2]*dp[i*2+1]%p*lucas(size[i]-1,size[i*2],p)%p;
        }
    }
    printf("%lld",dp[1]);
}

 

posted @ 2017-09-19 20:58  JSC!  阅读(213)  评论(0编辑  收藏  举报