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 }

猜你喜欢

转载自www.cnblogs.com/mjtcn/p/8982006.html