BZOJ3625: 小朋友和二叉树

传送门

Sol

f x f_x fx 表示权值为 x x x 的二叉树的个数
s x s_x sx 表示是否有 x x x 这种权值可以选择
那么
f n = ∑ i = 0 n ∑ j = 0 n − i f j f n − i − j s i f_n=\sum_{i=0}^{n}\sum_{j=0}^{n-i}f_jf_{n-i-j}s_i fn=i=0nj=0nifjfnijsi
构造
F ( x ) = ∑ i = 0 f i x i F(x)=\sum_{i=0}f_ix^i F(x)=i=0fixi
S ( x ) = ∑ i = 0 s i x i S(x)=\sum_{i=0}s_ix^i S(x)=i=0sixi
由于 s 0 = 0 , f 0 = 1 s_0=0,f_0=1 s0=0,f0=1
那么
F 2 ( x ) S ( x ) = F ( x ) − 1 F^2(x)S(x)=F(x)-1 F2(x)S(x)=F(x)1
所以可以求得
F ( x ) = 1 ± 1 − 4 S ( x ) 2 S ( x ) F(x)=\frac{1\pm \sqrt{1-4S(x)}}{2S(x)} F(x)=2S(x)1±14S(x)
由于 F ( 0 ) = 1 , S ( 0 ) = 0 F(0)=1,S(0)=0 F(0)=1,S(0)=0 所以
F ( x ) = 1 − 1 − 4 S ( x ) 2 S ( x ) F(x)=\frac{1- \sqrt{1-4S(x)}}{2S(x)} F(x)=2S(x)114S(x)
正是因为 S ( 0 ) = 0 S(0)=0 S(0)=0 没有办法求逆
所以化简得到
F ( x ) = 2 1 + 1 − 4 S ( x ) F(x)=\frac{2}{1+ \sqrt{1-4S(x)}} F(x)=1+14S(x) 2
开根(常数项是 1 1 1 所以不用二次剩余)+求逆即可

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn(4e5 + 5);
const int mod(998244353);
const int inv2(499122177);

inline int Pow(ll x, int y) {
    register ll ret = 1;
    for (; y; y >>= 1, x = x * x % mod)
        if (y & 1) ret = ret * x % mod;
    return ret;
}

inline void Inc(int &x, int y) {
    if ((x += y) >= mod) x -= mod;
}

int a[maxn], b[maxn], c[maxn], w[2][maxn], deg, r[maxn], l;

inline void Init(int n) {
	register int i, k, wn, iwn;
	for (deg = 1, l = 0; deg < n; deg <<= 1) ++l;
	for (i = 0; i < deg; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
	for (i = 1; i < deg; i <<= 1) {
		w[0][0] = w[1][0] = 1;
		wn = Pow(3, (mod - 1) / (i << 1)), iwn = Pow(wn, mod - 2);
		for (k = 1; k < i; ++k) {
			w[0][deg / i * k] = 1LL * w[0][deg / i * (k - 1)] * wn % mod;
			w[1][deg / i * k] = 1LL * w[1][deg / i * (k - 1)] * iwn % mod;
		}
	}
}

inline void NTT(int *p, int opt) {
	register int i, j, k, t, wn, x, y;
	for (i = 0; i < deg; ++i) if (r[i] < i) swap(p[r[i]], p[i]);
	for (i = 1; i < deg; i <<= 1)
		for(t = i << 1, j = 0; j < deg; j += t)
			for (k = 0; k < i; ++k) {
				wn = w[opt == -1][deg / i * k];
				x = p[j + k], y = 1LL * wn * p[i + j + k] % mod;
				p[j + k] = x + y, p[i + j + k] = x - y;
				if (p[j + k] >= mod) p[j + k] -= mod;
				if (p[i + j + k] < 0) p[i + j + k] += mod;
			}
	if (opt == -1) {
		wn = Pow(deg, mod - 2);
		for (i = 0; i < deg; ++i) p[i] = 1LL * p[i] * wn % mod;
	}
}

int n, m, f[maxn], g[maxn], s[maxn];

void Inv(int *p, int *q, int len) {
	if (len == 1) {
		q[0] = Pow(p[0], mod - 2);
		return;
	}
	Inv(p, q, len >> 1);
	register int i, tmp = len << 1;
	for (i = 0; i < len; ++i) a[i] = p[i], b[i] = q[i];
	Init(tmp), NTT(a, 1), NTT(b, 1);
	for (i = 0; i < tmp; ++i) a[i] = 1LL * a[i] * b[i] % mod * b[i] % mod;
	NTT(a, -1);
	for (i = 0; i < len; ++i) q[i] = (2LL * q[i] + mod - a[i]) % mod;
	for (i = 0; i < tmp; ++i) a[i] = b[i] = 0;
}

void Sqrt(int *p, int *q, int len) {
    if (len == 1) {
        q[0] = sqrt(p[0]);
        return;
    }
    Sqrt(p, q, len >> 1), Inv(q, c, len);   
    register int i, tmp = len << 1;
    for (i = 0; i < len; ++i) a[i] = p[i];
    Init(tmp), NTT(a, 1), NTT(c, 1);
    for (i = 0; i < tmp; ++i) a[i] = 1LL * a[i] * c[i] % mod;
    NTT(a, -1);
    for (i = 0; i < len; ++i) q[i] = 1LL * (q[i] + a[i]) % mod * inv2 % mod;
    for (i = 0; i < tmp; ++i) a[i] = c[i] = 0;
}

int main() {
	register int i, len, v;
	scanf("%d%d", &n, &m);
	for (i = 1; i <= n; ++i) scanf("%d", &v), ++s[v];
	for (len = 1; len <= m; len <<= 1);
	for (i = 1; i <= m; ++i) s[i] = 1LL * s[i] * 4 % mod, s[i] = mod - s[i];
	s[0] = 1, Sqrt(s, f, len), Inc(f[0], 1), Inv(f, g, len);
	for (i = 0; i < len; ++i) g[i] = 2LL * g[i] % mod;
	for (i = 1; i <= m; ++i) printf("%d\n", g[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值