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的数 组成一个完全二叉树使之满足小根堆性质的方案数
考虑dp
设i点的子结点数量为size[i]
则$dp[i]=C(s[i]-1,s[i*2])*f[i*2]*f[i*2+1]$
#include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long ll; ll n,p; ll dp[2000005],size[2000005],fac[1000005]; ll qpow(ll a,ll b,ll mod) { ll res=1; a=a%mod; while(b) { if(b&1)res=(res*a)%mod; b=b>>1; a=(a*a)%mod; } return res; } ll C(ll x,ll y,ll mod) { if(x<y)return 0; return fac[x]*qpow(fac[y],p-2,p)%p*qpow(fac[x-y],p-2,p)%p; } ll lucas(ll x,ll y,ll p) { if(!y)return 1; return C(x%p,y%p,p)*lucas(x/p,y/p,p)%p; } int main() { scanf("%lld%lld",&n,&p); fac[0]=fac[1]=1; for(int i=2;i<=n;i++)fac[i]=fac[i-1]*i%p; for(int i=n;i;i--) { size[i]=size[i<<1]+size[i<<1|1]+1; dp[i]=lucas(size[i]-1,size[i<<1],p); if(n>=(i<<1))dp[i]=dp[i]*dp[i<<1]%p; if(n>=(i<<1|1))dp[i]=dp[i]*dp[i<<1|1]%p; } //for(int i=1;i<=n;i++)cout<<dp[i]<<endl; cout<<dp[1]<<endl; return 0; }