P2606 [ZJOI2010]排列計數
阿新 • • 發佈:2019-05-16
inline 質數 ++ class 前驅 就是 並且 span 獨立 。
P2606 [ZJOI2010]排列計數
因為每個結點至多有一個前驅,所以我們可以發現這是一個二叉樹。現在我們要求的就是以1為根的二叉樹中,有多少種情況,滿足小根堆的性質。
設\(f(i)\)表示以\(i\)為根的子樹中滿足小根堆性質的情況,那麽就有:\(f(i)=f(ls)*f(rs)*C_{sum(i)-1}^{sum(ls)}\)。表示選出\(sum(ls)\)個結點來作為左兒子中的結點,並且左右兒子都滿足小根堆的性質。這裏左右兒子這兩個問題都是獨立的,所以可以直接運用乘法原理。
這裏求組合數可以直接用Lucas定理來求,Lucas定理為:若p是一個質數,那麽\(C_n^m=C_{\frac{n}{p}}^{\frac{m}{p}}*C_{n\mod p}^{m\mod p}\mod p\)
代碼如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll ; const int N = 2e6 + 5; ll n, p; ll inv[N], fac[N], s[N], f[N]; ll C(ll a, ll b) { if(a < b) return 0; if(a == b || b == 0) return 1; if(a < p && b < p) return inv[b] * inv[a - b] % p * fac[a] % p; return C(a % p, b % p) * C(a / p, b / p) % p; } ll qp(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % p; a = a * a % p; b >>= 1; } return ans ; } int main() { cin >> n >> p; fac[0] = 1; for(int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % p; for(int i = 1; i <= n; i++) inv[i] = qp(fac[i], p - 2) ; for(int i = 1; i <= n; i++) s[i] = 1; for(int i = n; i >= 2; i--) s[i >> 1] += s[i] ; for(int i = n; i >= 1; i--) { int ls = i << 1, rs = i << 1 | 1; if(f[ls] && f[rs]) f[i] = f[ls] * f[rs] % p * C(s[i] - 1, s[ls]) % p; else if(f[ls]) f[i] = f[ls] ; else f[i] = 1; } cout << f[1] ; return 0; }
P2606 [ZJOI2010]排列計數