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 排列计数 组合数
组合数学+lucas定理+逆元 BZOJ2111 [ZJOI2010]Perm 排列计数
bzoj 2111: [ZJOI2010]Perm 排列计数 (dp+卢卡斯定理)