BZOJ3684 大朋友與多叉樹(拉格朗日反演)
阿新 • • 發佈:2020-07-29
BZOJ3684 大朋友與多叉樹(拉格朗日反演)
題目大意
我們的大朋友很喜歡電腦科學,而且尤其喜歡多叉樹。對於一棵帶有正整數點權的有根多叉樹,如果它滿足這樣的性質,我們的大朋友就會將其稱作神犇的:點權為1的結點是葉子結點;對於任一點權大於1的結點u,u的孩子數目deg[u]屬於集合D,且u的點權等於這些孩子結點的點權之和。
給出一個整數s,你能求出根節點權值為s的神犇多叉樹的個數嗎?請參照樣例以更好的理解什麼樣的兩棵多叉樹會被視為不同的。
我們只需要知道答案關於950009857(453*2^21+1,一個質數)取模後的值。
資料範圍
1<=m<s<=10^5, 2<=d[i]<=s
解題思路
拉格朗日反演經典題
寫出遞推式
\[T(x) = x+\sum_{i\in S}T(x)^i \]
使用拉格朗日反演
\[x = T(x)-\sum_{i \in S}T(x)^i \]
設 \(G(x)=T(x)\),\(F(x)=x-\sum_{i\in S}x^i\)
有 \(F(G(X)) = x\),所以可以直接求出 \([x^n]G(x)=\frac 1n[x^{n-1}]{\left(\frac{x}{F(x)}\right)}^n\)
程式碼
#include <queue> #include <vector> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define MP make_pair #define ll long long #define fi first #define se second using namespace std; template <typename T> void read(T &x) { x = 0; bool f = 0; char c = getchar(); for (;!isdigit(c);c=getchar()) if (c=='-') f=1; for (;isdigit(c);c=getchar()) x=x*10+(c^48); if (f) x=-x; } template<typename F> inline void write(F x, char ed = '\n') { static short st[30];short tp=0; if(x<0) putchar('-'),x=-x; do st[++tp]=x%10,x/=10; while(x); while(tp) putchar('0'|st[tp--]); putchar(ed); } template <typename T> inline void Mx(T &x, T y) { x < y && (x = y); } template <typename T> inline void Mn(T &x, T y) { x > y && (x = y); } const int N = 333400; const int P = 950009857; int r[N], lim, n; ll A[N], B[N], C[N], D[N], E[N], f[N], g[N]; ll inv[N]; void init(void) { inv[0] = inv[1] = 1; for (int i = 2;i <= 2 * n; i++) inv[i] = inv[P % i] * (P - P / i) % P; } ll fpw(ll x, ll mi) { ll res = 1; for (; mi; mi >>= 1, x = x * x % P) if (mi & 1) res = res * x % P; return res; } void NTT(ll *a, int ty) { for (int i = 0;i < lim; i++) if (r[i] > i) swap(a[i], a[r[i]]); for (int i = 1;i < lim; i <<= 1) { ll wn = fpw(7, (P - 1) / (i << 1)); for (int j = 0;j < lim; j += (i << 1)) { ll w = 1; for (int k = 0;k < i; k++, w = w * wn % P) { ll x = a[j+k], y = a[i+j+k] * w % P; a[j+k] = (x + y) % P, a[i+j+k] = (x - y + P) % P; } } } if (ty == -1) { ll inv = fpw(lim, P - 2); for (int i = 0;i < lim; i++) a[i] = a[i] * inv % P; reverse(a + 1, a + lim); } } /* F(G) - A = 0; F(G0) - A = 0; 0 = T(G0)+T(G0)'(G-G0) G = G0-T(G0)/T(G0)' 1/G0 - A = 0; G = G0 + (1/G0-A)G0^2 G = G0 + G0 - G0^2A G = G0(2 - A*G0) */ void Inv(ll *a, ll *b, int deg) { b[0] = fpw(a[0], P - 2); int len; for (len = 2;len < (deg << 1); len <<= 1) { lim = len << 1; for (int i = 1;i < lim; i++) r[i] = (r[i>>1]>>1) | ((i & 1) ? len : 0); for (int i = 0;i < len; i++) A[i] = a[i], B[i] = b[i]; NTT(A, 1), NTT(B, 1); for (int i = 0;i < lim; i++) b[i] = (2 + (P - A[i]) * B[i]) % P * B[i] % P; NTT(b, -1); for (int i = len;i < lim; i++) b[i] = 0; } for (int i = 0;i < len; i++) A[i] = B[i] = 0; } void chick(ll *a, int deg) { for (int i = deg - 1;i >= 1; i--) a[i] = a[i-1] * inv[i] % P; a[0] = 0; } void door(ll *a, int deg) { for (int i = 1;i < deg; i++) a[i-1] = a[i] * i % P; a[deg - 1] = 0; } void Ln(ll *a, int deg) { Inv(a, C, deg), door(a, deg); NTT(a, 1), NTT(C, 1); for (int i = 0;i < lim; i++) a[i] = a[i] * C[i] % P; NTT(a, -1), chick(a, deg); } /* F(G) - A = 0; F(G0) - A = 0; 0 = T(G0)+T(G0)'(G-G0) G = G0-T(G0)/T(G0)' 1/G0 - A = 0; G = G0 + (LnG0-A)G0 G = -(-1+LnG0-A)G0 */ void Exp(ll *a, ll *b, int deg) { b[0] = 1; int len; for (len = 2;len < (deg << 1); len <<= 1) { lim = len << 1; for (int i = 0;i < len; i++) D[i] = b[i]; Ln(D, len); D[0] = (a[0] + 1 - D[0] + P) % P; for (int i = 1;i < len; i++) D[i] = (a[i] - D[i] + P) % P; NTT(D, 1), NTT(b, 1); for (int i = 0;i < lim; i++) b[i] = b[i] * D[i] % P; NTT(b, -1); for (int i = len;i < lim; i++) b[i] = 0; } } ll G[N]; void lage() { Inv(f, E, n), Ln(E, n); for (int i = 0;i < n; i++) E[i] = E[i] * n % P; Exp(E, G, n); } int m; int main() { read(n), read(m), init(); f[0] = 1; for (int i = 0, t;i < m; i++) read(t), f[t-1] += P - 1; lage(); write(G[n-1] * inv[n] % P); return 0; }