[Lucas][树形dp][组合] Bzoj P2111 Perm 排列计数
Description
称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
题解
- 把i/2看作i和i+1的父亲,然后这样就会形成一棵完全二叉树
- 那么就可以考虑树形dp,设f[i]表示从i到n的方案数,size[i]表示第i个数的子树大小
- 那么如果i*2>n,那么f[i]=1,如果i*2==n,那么f[i]=f[i*2]
- 如果i*2+1<=n,那么f[i]=f[i*2]*f[i*2+1]*C(size[i*2],size[i]-1)
- 还是要用Lucas定理来搞
代码
1 #include <cstdio> 2 #include <iostream> 3 #define N 1000010 4 #define ll long long 5 using namespace std; 6 int n,p,size[N]; 7 ll inv[N],fac[N],f[N]; 8 ll C(ll n,ll m) { return m>n?0:(n<p?fac[n]*inv[m]%p*inv[n-m]%p:C(n/p,m/p)*C(n%p,m%p)); } 9 int main() 10 { 11 scanf("%d%d",&n,&p),fac[0]=inv[0]=inv[1]=1; 12 for (int i=1;i<=min(n,p-1);i++) fac[i]=fac[i-1]*i%p; 13 for (int i=2;i<=min(n,p-1);i++) inv[i]=(p-p/i)*inv[p%i]%p; 14 for (int i=2;i<=min(n,p-1);i++) inv[i]=(inv[i]*inv[i-1])%p; 15 for (int i=n;i;i--) 16 if (i*2+1<=n) size[i]=1+size[i*2]+size[i*2+1],f[i]=f[2*i]*f[2*i+1]%p*C(size[i]-1,size[2*i])%p; 17 else if (i*2<=n) size[i]=1+size[i*2],f[i]=f[2*i]; 18 else size[i]=f[i]=1; 19 printf("%lld",f[1]); 20 }