组合数学+lucas定理+逆元 BZOJ2111 [ZJOI2010]Perm 排列计数
2111: [ZJOI2010]Perm 排列计数
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 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 23Sample Output
16HINT
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 #include2 #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(nn return 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 }