Solution [ZJOI2010]排列计数
题目大意:求 (1-n) 的排列 (p) 中,有多少个排列满足 (forall iin [2,n],p_i > p_{lfloorfrac{i}{2} floor}),对给定质数 (m) 取模。
分析:
(p_i > p_{lfloorfrac{i}{2} floor}),反过来 (forall x,p_x<p_{2x},p_{2x+1}),那么问题变成一棵 (n) 个节点的完全二叉树,将数 (1-n) 分配给每一个节点,使得每个父节点都比它两个子节点权值要小的方案数。
进一步,我们不关心数究竟是啥,我们只关心它们之间的相对大小关系。
记 (siz[u]) 表示以 (u) 为根的子树大小,(f[u]) 表示将 (siz[u]) 个互不相同的数分配给以 (u) 为根的子树的合法方案数。记 (ls,rs) 为左右儿子。
那么 (f[u]=f[ls]cdot f[rs] cdot inom{siz[ls]+siz[rs]}{siz[ls]})
坑点在于,(n) 有可能比 (m) 大,此时需要使用 ( ext{Lucas}) 定理
#include <cstdio>
using namespace std;
const int maxn = 1e6 + 100;
int n,mod;
inline int add(const int a,const int b){return (a + b) % mod;}
inline int mul(const int a,const int b){return (1ll * a * b) % mod;}
inline int qpow(int base,int b){
int res = 1;
while(b){
if(b & 1)res = mul(res,base);
base = mul(base,base);
b >>= 1;
}
return res;
}
inline int inv(const int x){return qpow(x,mod - 2);}
int fac[maxn];
inline void init(){
fac[0] = 1;
for(int i = 1;i <= n;i++)fac[i] = mul(fac[i - 1],i);
}
extern inline int C(const int n,const int m);
inline int lucas(const int n,const int m){
if(n < mod)return C(n,m);
return mul(C(n / mod,m / mod),C(n % mod,m % mod));
}
inline int C(const int n,const int m){return n < mod ? mul(fac[n],inv(mul(fac[m],fac[n - m]))) : lucas(n,m);}
int f[maxn << 2],siz[maxn << 2];
#define ls (u << 1)
#define rs (u << 1 | 1)
inline void dp(const int u){
f[u] = 1;
if(u < 1 || u > n)return;
siz[u] = 1;
dp(ls);
dp(rs);
siz[u] += siz[ls];
siz[u] += siz[rs];
f[u] = mul(
C(siz[u] - 1,siz[ls]),
mul(f[ls],f[rs])
);
}
#undef ls
#undef rs
int main(){
scanf("%d %d",&n,&mod);
init();
dp(1);
printf("%d
",f[1]);
return 0;
}