题面:bzoj3625
题意:一棵带点权的树的权值为所有点的权值和。给定 m m m,求点权在给定集合中的权值为 s ( 1 ≤ s ≤ m ) s(1 \le s \le m) s(1≤s≤m)的二叉树的个数。
题解:设 f ( i ) f(i) f(i)为权值为 i i i的二叉树个数, c ( i ) c(i) c(i)为集合中数的生成函数。
f ( n ) = ∑ i = 1 n c ( i ) ∑ j = 0 i f ( j ) ∗ f ( n − i − j ) f(n) = \sum _{i=1} ^n c(i) \sum _{j=0} ^i f(j) * f(n - i - j) f(n)=∑i=1nc(i)∑j=0if(j)∗f(n−i−j)
f ( 0 ) = 1 f(0)=1 f(0)=1
构造生成函数 F ( x ) = ∑ i = 0 ∞ f ( i ) x i F(x) = \sum _{i=0} ^{\infty} f(i) x^i F(x)=∑i=0∞f(i)xi, C ( x ) = ∑ i = 0 ∞ c ( i ) x i C(x) = \sum _{i=0} ^{\infty} c(i) x^i C(x)=∑i=0∞c(i)xi
F ( x ) = C ( x ) ∗ F ( x ) ∗ F ( x ) + 1 F(x) = C(x) * F(x) * F(x) + 1 F(x)=C(x)∗F(x)∗F(x)+1
解得 F ( x ) = 1 ± 1 − 4 C ( x ) 2 C ( x ) F(x) = \frac {1 \pm \sqrt{1-4C(x)}} {2C(x)} F(x)=2C(x)1±1−4C(x)
当取负号时多项式才可逆,上下再同乘共轭根式。
F ( x ) = 1 − 1 − 4 C ( x ) 2 C ( x ) = 1 − ( 1 − 4 C ( x ) ) 2 C ( x ) ( 1 + 1 − 4 C ( x ) ) = 4 C ( x ) 2 C ( x ) ( 1 + 1 − 4 C ( x ) ) = 2 1 + 1 − 4 C ( x ) F(x) = \frac {1 - \sqrt {1 - 4C(x)}} {2C(x)} = \frac {1- (1 - 4C(x))} {2C(x)(1 + \sqrt {1 - 4C(x)})} = \frac {4C(x)} {2C(x)(1+\sqrt {1 - 4C(x)})} = \frac 2 {1 + \sqrt {1 - 4C(x)}} F(x)=2C(x)1−1−4C(x)=2C(x)(1+1−4C(x))1−(1−4C(x))=2C(x)(1+1−4C(x))4C(x)=1+1−4C(x)2
套多项式开根,求逆模板即可。
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1; char c; c = getchar() ;
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
return x * f ;
}
typedef long long ll ;
const int maxn = 1e6 + 10 ;
int inv2 ;
namespace Polynomial {
const int mod = 998244353, g = 3, ginv = 332748118 ;
inline ll power (ll x, int y) {
ll res = 1 ;
while (y) {
if (y & 1) res = res * x % mod ;
x = x * x % mod; y >>= 1 ;
}
return res ;
}
inline void ntt (int a[], int n, int f) {
for (int i = 0, j = 0; i < n; i ++) {
if (i > j) swap (a[i], a[j]) ;
for (int t = n >> 1; (j ^= t) < t; t >>= 1) ;
}
static int w[maxn] ;
for (int i = 0; i <= n; i ++)
w[i] = power (f ? ginv : g, (mod - 1) / n * i) ;
for (int i = 2; i <= n; i <<= 1) {
for (int j = 0; j < n; j += i)
for (int k = 0; k < (i >> 1); k ++) {
int A = a[j + k], B = 1ll * w[n / i * k] * a[j + k + (i >> 1)] % mod ;
a[j + k] = (A + B) % mod; a[j + k + (i >> 1)] = (A - B + mod) % mod ;
}
}
if (f) {
int invn = power (n, mod - 2) ;
for (int i = 0; i < n; i ++) a[i] = 1ll * a[i] * invn % mod ;
}
}
int a[maxn], b[maxn], A[maxn], B[maxn] ;
inline void inv (int a[], int b[], int len) {
if (len == 1) {b[0] = power (a[0], mod - 2); return;}
inv (a, b, len >> 1) ;
for (int i = 0; i < len; i ++) A[i] = a[i], B[i] = b[i] ;
ntt (A, len << 1, 0); ntt (B, len << 1, 0) ;
for (int i = 0; i < (len << 1); i ++) A[i] = 1ll * A[i] * B[i] % mod * B[i] % mod ;
ntt (A, len << 1, 1) ;
for (int i = 0; i < len; i ++) b[i] = (2ll * b[i] % mod - A[i] + mod) % mod ;
for (int i = 0; i < (len << 1); i ++) A[i] = B[i] = 0 ;
}
int C[maxn], D[maxn] ;
inline void sqrt (int a[], int b[], int len) {
if (len == 1) {b[0] = a[0]; return;}
sqrt (a, b, len >> 1) ;
for (int i = 0; i <= len; i ++) C[i] = a[i] ;
inv (b, D, len) ;
ntt (C, len << 1, 0); ntt (D, len << 1, 0) ;
for (int i = 0; i < (len << 1); i ++) D[i] = 1ll * D[i] * C[i] % mod ;
ntt (D, len << 1, 1) ;
for (int i = 0; i < len; i ++) b[i] = 1ll * (D[i] + b[i]) % mod * inv2 % mod ;
for (int i = 0; i <= (len << 1); i ++) C[i] = D[i] = 0 ;
}
}
using namespace Polynomial ;
int n, m, c[maxn], d[maxn] ;
int main() {
inv2 = power (2, mod - 2) ;
n = read(); m = read() ;
for (int i = 1; i <= n; i ++) ++ c[read()] ;
int N ;
for (N = 1; N <= m; N <<= 1) ;
for (int i = 0; i < N; i ++) c[i] = (-4ll * c[i] + mod) % mod ;
c[0] ++; sqrt (c, d, N) ;
for (int i = 0; i < N; i ++) c[i] = 0 ;
d[0] = (d[0] + 1) % mod; inv (d, c, N) ;
for (int i = 1; i <= m; i ++) printf("%d\n", (int)(2ll * c[i] % mod)) ;
return 0 ;
}
(自己写的多项式开根板子常数巨大)