题目描述
称一个1,2,…,N的排列P1,P2…,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,…N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
题解
看到这种式子要马上反应到是一颗满二叉树,这里就是一个小根堆。考虑这种树形态的DP一般是从两个子树推上来,考虑一个小根堆的子树任意形态都没有问题,所以只要左子树的形态数*右子树的形态数就行了,然而我们规定了点的编号,所以还要乘上一个组合数表示哪些点放在了左子树
代码
#include <bits/stdc++.h>
#define maxn 1000005
#define MAXN 1000005
#define LL long long
#define INF 0x3f3f3f3f
#define I inline
#define re register
using namespace std;
int read(){
int res; bool f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=0; res=c^48;
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res;
}
int mod;
int Pow(int x,int k){
int res=1;
while(k){
if(k&1) res=1ll*res*x%mod;
x=1ll*x*x%mod; k>>=1;
}
return res;
}
int n,size[maxn],f[maxn],jc[maxn],ny[maxn];
int C(int a,int b){
return 1ll*jc[a]*ny[b]%mod*ny[a-b]%mod;
}
int main(){
n=read(); mod=read();
jc[0]=1; for(int i=1;i<=n;i++) jc[i]=1ll*jc[i-1]*i%mod;
ny[n]=Pow(jc[n],mod-2); for(int i=n;i;i--) ny[i-1]=1ll*ny[i]*i%mod;
for(int i=n;i;i--){
size[i]++;
size[i/2]+=size[i];
}
for(int i=n;i;i--){
if(2*i+1>n) f[i]=1;
else f[i]=1ll*f[i*2]*f[i*2+1]%mod*C(size[i]-1,size[2*i])%mod;
}
printf("%d\n",f[1]);
return 0;
}