CF438E The Child and Binary Tree——生成函数
Posted joker-yza
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF438E The Child and Binary Tree——生成函数相关的知识,希望对你有一定的参考价值。
题面
解析
一开始又把题读错了...
设$g_i=1/0$表示数$i$是否在$c$中出现过,$f_i$表示权值和为$i$的二叉树个数,有下式:$$f_i=sum_{j=1}^{m}g_jsum_{k=0}^{i-j}f_k f_{i-j-k}$$
设$F(x)=sum_{i=0}^{infty}f_i x^i$, $G(x)=sum_{i=0}^{infty}g_i x^i$,有:$$F=G*F^2 + 1$$
后面$+1$是因为$g_0=0$而$f_0=1$
求根公式:$$F = frac{1 pm sqrt{1-4G}}{2G} \ F=frac{2}{1pm sqrt{1-4G}}$$
取$+$号:$lim_{x o 0}F(x)=1$,符合题意
取$-$号:$lim_{x o 0}F(x)=infty$,不符题意,舍去
故:$$F=frac{2}{1 + sqrt{1-4G}}$$
多项式开根+多项式求逆
$O(N log N)$
代码:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; const int maxn = 200005, mod = 998244353, g = 3; inline int read() { int ret, f=1; char c; while((c=getchar())&&(c<‘0‘||c>‘9‘))if(c==‘-‘)f=-1; ret=c-‘0‘; while((c=getchar())&&(c>=‘0‘&&c<=‘9‘))ret=(ret<<3)+(ret<<1)+c-‘0‘; return ret*f; } int add(int x, int y) { return x + y < mod? x + y: x + y - mod; } int rdc(int x, int y) { return x - y < 0? x - y + mod: x - y; } ll qpow(ll x, int y) { ll ret = 1; while(y) { if(y&1) ret = ret * x % mod; x = x * x % mod; y >>= 1; } return ret; } int n, m, lim, bit, rev[maxn<<1]; ll ginv, inv2, F[maxn<<1], G[maxn<<1], c[maxn<<1], iv[maxn<<1], f[maxn<<1]; void init() { ginv = qpow(g, mod - 2); inv2 = (mod + 1) >> 1; } void NTT_init(int x) { lim = 1; bit = 0; while(lim <= x) { lim <<= 1; ++ bit; } for(int i = 1; i < lim; ++i) rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (bit - 1)); } void NTT(ll *x, int y) { for(int i = 1; i < lim; ++i) if(i < rev[i]) swap(x[i], x[rev[i]]); ll wn, w, u, v; for(int i = 1; i < lim; i <<= 1) { wn = qpow((y == 1)? g: ginv, (mod - 1) / (i << 1)); for(int j = 0; j < lim; j += (i << 1)) { w = 1; for(int k = 0; k < i; ++k) { u = x[j+k]; v = x[j+k+i] * w % mod; x[j+k] = add(u, v); x[j+k+i] = rdc(u, v); w = w * wn % mod; } } } if(y == -1) { ll linv = qpow(lim, mod - 2); for(int i = 0; i < lim; ++i) x[i] = x[i] * linv % mod; } } void get_inv(ll *x, ll *y, int len) { if(len == 1) { x[0] = qpow(y[0], mod - 2); return ; } get_inv(x, y, (len + 1) >> 1); for(int i = 0; i < len; ++i) c[i] = y[i]; NTT_init(len << 1); NTT(x, 1); NTT(c, 1); for(int i = 0; i < lim; ++i) { x[i] = rdc(add(x[i], x[i]), (c[i] * x[i] % mod) * x[i] % mod); c[i] = 0; } NTT(x, -1); for(int i = len; i < lim; ++i) x[i] = 0; } void get_sqr(ll *x, ll *y, int len) { if(len == 1) { x[0] = 1; return ; } get_sqr(x, y, (len + 1) >> 1); get_inv(iv, x, len); for(int i = 0; i < len; ++i) c[i] = y[i]; NTT_init(len << 1); NTT(c, 1); NTT(iv, 1); for(int i = 0; i < lim; ++i) { c[i] = c[i] * iv[i] % mod; iv[i] = 0; } NTT(c, -1); for(int i = 0; i < len; ++i) x[i] = add(c[i], x[i]) * inv2 % mod; for(int i = 0; i < lim; ++i) c[i] = 0; } int main() { n = read(); m = read(); init(); int x; for(int i = 1; i <= n; ++i) { x = read(); G[x] = 1; } for(int i = 0; i <= 100000; ++i) G[i] = rdc(0, 4 * G[i] % mod); G[0] = 1; get_sqr(F, G, 100001); F[0] = add(F[0], 1); get_inv(f, F, 100001); for(int i = 1; i <= m; ++i) printf("%d ", add(f[i], f[i])); return 0; }
以上是关于CF438E The Child and Binary Tree——生成函数的主要内容,如果未能解决你的问题,请参考以下文章
CF438E The Child and Binary Tree
bzoj 3625(CF 438E)The Child and Binary Tree——多项式开方
CF438E The Child and Binary Tree(生成函数+多项式开根+多项式求逆)