[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 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/11335187.html