2111: [ZJOI2010]Perm 排列计数

Posted HWIM

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2111: [ZJOI2010]Perm 排列计数相关的知识,希望对你有一定的参考价值。

2111: [ZJOI2010]Perm 排列计数

链接

思路 

  lucas定理+dp。

  f[i] 表示以i为根的子树,的方案数。siz[i]为大小。即所有的取值。

  f[i] = f[i*2] * f[i*2+1] * C(siz[i]-1,siz[i*2])。表示从所有的可以取值个数减去根节点(siz[i]-1)中给左孩子的取值的个数(siz[i*1])。

 

代码

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<iostream>
 4 
 5 using namespace std;
 6 typedef long long LL;
 7 const int N = 2000100;
 8 
 9 LL f[N],inv[N],dp[N],siz[N],t[N],p;
10 int n,mx;
11 
12 void init() {
13     f[0] = f[1] = inv[0] = inv[1] = t[0] = t[1] = 1;
14     for (int i=2; i<=mx; ++i) {
15         f[i] = (f[i-1] * i) % p;
16         inv[i] = (-(p/i)*inv[p%i]) % p;
17         inv[i] = (inv[i] + p) % p;
18         t[i] = t[i-1] * inv[i] % p; 
19     //    if (inv[i] * i % p != 1) cout << ‘a‘; 
20     }
21 }
22 LL Lucas(LL a,LL b) {
23     if (a < b) return 0;
24     if (a < p && b < p) 
25         return f[a]*t[b]%p*t[a-b]%p;
26     return Lucas(a/p,b/p)*Lucas(a%p,b%p)%p;
27 }
28 int main() {
29     cin >> n >> p;
30     mx = min(LL(n),p);
31     init();
32     for (int i=n; i>=1; --i) {
33         siz[i] = siz[i<<1] + siz[i<<1|1] + 1;
34         dp[i] = Lucas(siz[i]-1,siz[i<<1]);
35         if ((i<<1)<=n) dp[i] = (dp[i] * dp[i<<1]) % p;
36         if ((i<<1|1)<=n) dp[i] = (dp[i] * dp[i<<1|1]) % p; 
37     }
38     cout << dp[1];
39     return 0;
40 }

 

以上是关于2111: [ZJOI2010]Perm 排列计数的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2111[ZJOI2010]Perm 排列计数 组合数

bzoj2111: [ZJOI2010]Perm 排列计数

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

bzoj 2111: [ZJOI2010]Perm 排列计数 (dp+卢卡斯定理)

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

BZOJ 2111 [ZJOI2010]Perm 排列计数