1. 程式人生 > >2111: [ZJOI2010]Perm 排列計數

2111: [ZJOI2010]Perm 排列計數

stream urn style href Go target sin amp code

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 排列計數