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是一个质数。
令fa[i]=i/2,就出现了一棵树。
f[i]表示i的子树的排列方案数。
siz[i]表示i的子树大小。
f[x]*=f[to[i]]*C(siz[x]-1,siz[to[i]])。siz[x]是不断更新的。
其中那个组合数的含义是每个儿子交错排列的方案数。
有坑点,mod可能小于n。逆元需要分类讨论。
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 1000050 typedef long long ll; ll fac[N],inv[N],f[N]; int siz[N],n,m,mod; int head[N],to[N<<1],nxt[N<<1],cnt; inline void add(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; } ll qp(ll x,int y) {ll re=1; for(;y;y>>=1,x=x*x%mod) if(y&1) re=re*x%mod; return re;} void init() { int i; for(fac[0]=1,i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; if(mod<=n) { inv[mod-1]=mod-1; for(i=mod-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod; for(i=mod;i<=n;i++) inv[i]=inv[i%mod]; }else { inv[n]=qp(fac[n],mod-2); for(i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod; } } ll C(int x,int y) { if(x<y) return 0; if(x<mod&&y<mod) return fac[x]*inv[y]%mod*inv[x-y]%mod; return C(x%mod,y%mod)*C(x/mod,y/mod)%mod; } void dfs(int x) { int i; f[x]=1; siz[x]=1; for(i=head[x];i;i=nxt[i]) { dfs(to[i]); siz[x]+=siz[to[i]]; f[x]=f[x]*C(siz[x]-1,siz[to[i]])%mod*f[to[i]]%mod; } } int main() { scanf("%d%d",&n,&mod); init(); int i; for(i=2;i<=n;i++) add(i>>1,i); dfs(1); printf("%lld ",f[1]); }