【BZOJ2111】[ZJOI2010]Perm 排列計數 組合數
阿新 • • 發佈:2017-08-15
文件的 二叉堆 合數 name string stream main 文件 ...
【BZOJ2111】[ZJOI2010]Perm 排列計數
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是一個質數。
題解:題意可轉化為:求n個節點能構成的完全二叉堆的個數。顯然我們可以求出左右兩棵子樹的大小,然後分別遞歸下去即可。
細節有點多~
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const int maxn=1000010; int m=1000000; ll n,p; ll jc[maxn],jcc[maxn],ine[maxn],f[maxn]; int Log[maxn]; ll C(ll a,ll b) { if(a<b) return 0; if(!b) return 1; if(a<p&&b<p) return jc[a]*jcc[b]%p*jcc[a-b]%p; return C(a%p,b%p)*C(a/p,b/p)%p; } ll calc(ll x) { if(f[x]) return f[x]; ll a=x-(1<<Log[x+1])+1; if(a<(1<<Log[x+1]-1)) a=(1<<Log[x+1]-1)-1+a; else a=(1<<Log[x+1])-1; return f[x]=C(x-1,a)*calc(a)%p*calc(x-a-1)%p; } int main() { scanf("%lld%lld",&n,&p); if(m>=p) m=p-1; ll i; jc[0]=jcc[0]=1,ine[0]=ine[1]=1; for(i=2;i<=m;i++) ine[i]=(p-(p/i)*ine[p%i]%p)%p; for(i=1;i<=m;i++) jc[i]=jc[i-1]*i%p,jcc[i]=jcc[i-1]*ine[i]%p; for(i=2;i<=n+1;i++) Log[i]=Log[i>>1]+1; f[0]=f[1]=1; printf("%lld",calc(n)); return 0; }
【BZOJ2111】[ZJOI2010]Perm 排列計數 組合數