组合数学+lucas定理+逆元 BZOJ2111 [ZJOI2010]Perm 排列计数


2111: [ZJOI2010]Perm 排列计数

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 2118  Solved: 563
[Submit][Status][Discuss]

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的排列能组成多少种小根堆。

啊!!!不想写题解……

f[i]=f[i<<1]*f[i<<1|1]*c(s[i]-1,s[i<<1]),答案为f[1]。

要求一下逆元的……

 1 #include
 2 #include
 3 #include
 4 #include
 5 using namespace std;
 6 int n,p;
 7 int fact[1000010],ifact[1000010],s[1000010],f[1000010];
 8 int qpow(int a,int b,int mod){
 9     int ans;
10     for(ans=1;b;b>>=1,a=(long long)a*a%mod)
11         if(b&1) ans=(long long)ans*a%mod;
12     return ans;
13 }
14 void prework(){
15     ifact[0]=1;//!!!!!!
16     fact[1]=1;
17     ifact[1]=1;
18     for(int i=2;i<=n;i++){
19         fact[i]=(long long)i*fact[i-1]%p;
20         ifact[i]=qpow(fact[i],p-2,p);
21     }
22 }
23 int zuhe(int nn,int mm,int mod){
24     if(nnreturn 0;
25     return (long long)fact[nn]*ifact[mm]%mod*ifact[nn-mm]%mod;
26 }
27 int lucas(int nn,int mm,int mod){
28     if(!nn&&!mm) return 1;
29     return (long long)zuhe(nn%mod,mm%mod,mod)*lucas(nn/mod,mm/mod,mod)%mod;
30 }
31 int main(){
32     scanf("%d%d",&n,&p);
33     prework();
34     for(int i=n;i>0;i--){
35         s[i]=s[i<<1]+s[i<<1|1]+1;
36         f[i]=lucas(s[i]-1,s[i<<1],p);
37         if((i<<1)<=n) f[i]=(long long)f[i]*f[i<<1]%p;
38         if((i<<1|1)<=n) f[i]=(long long)f[i]*f[i<<1|1]%p;
39     }
40     printf("%d",f[1]);
41     return 0;
42 }