[Lucas][树形dp][组合] Bzoj P2111 Perm 排列计数

Posted comfortable

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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 

 

以上是关于[Lucas][树形dp][组合] Bzoj P2111 Perm 排列计数的主要内容,如果未能解决你的问题,请参考以下文章

bzoj2111[ZJOI2010]Perm 排列计数 dp+Lucas定理

组合数学+lucas定理+逆元 BZOJ2111 [ZJOI2010]Perm 排列计数

[CTSC2017][bzoj4903] 吉夫特 [状压dp+Lucas定理]

BZOJ 2159: Crash 的文明世界(树形dp+第二类斯特林数+组合数)

bzoj 2111 [ZJOI2010]Perm 排列计数(DP+lucas定理)

bzoj 4903: [Ctsc2017]吉夫特lucas+状压dp