Luogu2606[ZJOI2010] 排列計數
阿新 • • 發佈:2018-12-19
排列計數
題目描述
稱一個1,2,…,N的排列P1,P2…,Pn是Magic的,當且僅當2<=i<=N時,Pi>Pi/2. 計算1,2,…N的排列中有多少是Magic的,答案可能很大,只能輸出模P以後的值
輸入輸出格式
輸入格式:
輸入檔案的第一行包含兩個整數 n和p,含義如上所述。
輸出格式:
輸出檔案中僅包含一個整數,表示計算1,2,⋯, ���的排列中, Magic排列的個數模 p的值。
輸入輸出樣例
輸入樣例#1:
20 23
輸出樣例#1:
16
說明
100%的資料中,1 ≤N ≤ 10^6, P≤ 10^9,p是一個質數。
題解
條件是,反過來看就是,唉這不是個滿二叉小根堆嗎。
那麼問題就變成了點數為的滿二叉小根堆的計數問題,表示以為根的滿二叉小根堆的個數,有轉移方程如下:
陣列的話在時順帶維護一下即可。
由於模數不確定,且一定為質數,所以我們使用定理:
程式碼
#include<bits/stdc++.h>
#define ll long long
#define ls (i<<1)
#define rs (ls|1)
using namespace std;
const int M=1e6+5;
int siz[M],n,mod,mx;
ll fac[M],inv[M],dp[M];
ll power(ll x,ll p){ll r=1;for(;p;p>>=1,x=x*x%mod)if(p&1)r=r*x%mod;return r;}
ll C(int n,int m)
{
if(m>n)return 0;
if(n<mod&&m<mod)return fac[n]*inv[n-m]%mod*inv[m]%mod;
return C(n/mod,m/mod)*C(n%mod,m%mod)%mod;
}
void in(){scanf("%d%d",&n,&mod);}
void ac()
{
mx=min(mod-1,n);
fac[0]=1;for(int i=1;i<=mx;++i)fac[i]=fac[i-1]*i%mod;
inv[mx]=power(fac[mx],mod-2);for(int i=mx-1;i>=0;--i)inv[i]=(i+1)*inv[i+1]%mod;
for(int i=n;i;--i)
{
siz[i]=1;if(ls<=n)siz[i]+=siz[ls];if(rs<=n)siz[i]+=siz[rs];
if(rs<=n)dp[i]=C(siz[i]-1,siz[ls])*dp[ls]%mod*dp[rs]%mod;
else if(ls<=n)dp[i]=dp[ls];
else dp[i]=1;
}
printf("%lld",dp[1]);
}
int main()
{
in(),ac();
}