[Codeforces Round #250 (Div. 1) -E] The Child and Binary Tree

10 篇文章 0 订阅
4 篇文章 0 订阅
Codeforces传送门
洛谷传送门

题目翻译

我们的小朋友很喜欢计算机科学,而且尤其喜欢二叉树。 考虑一个含有 n n n个互异正整数的序列 c [ 1 ] , c [ 2 ] , . . . , c [ n ] c[1],c[2],...,c[n] c[1],c[2],...,c[n]。如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合 { c [ 1 ] , c [ 2 ] , . . . , c [ n ] } \{c[1],c[2],...,c[n]\} {c[1],c[2],...,c[n]}中,我们的小朋友就会将其称作神犇的。并且他认为,一棵带点权的树的权值,是其所有顶点权值的总和。 给出一个整数 m m m,你能对于任意的 s ( 1 ≤ s ≤ m ) s(1\le s\le m) s(1sm)计算出权值为 s s s的神犇二叉树的个数吗?请参照样例以更好的理解什么样的两棵二叉树会被视为不同的。 我们只需要知道答案关于 998244353 998244353 998244353取模后的值。

输入第一行有 2 2 2个整数 n , m ( 1 ≤ n ≤ 1 0 5 ; 1 ≤ m ≤ 1 0 5 ) n,m(1\le n\le 10^5; 1\le m\le 10^5) n,m(1n105;1m105)。 第二行有 n n n个用空格隔开的互异的整数 c [ 1 ] , c [ 2 ] , . . . , c [ n ] ( 1 ≤ c [ i ] ≤ 1 0 5 ) c[1],c[2],...,c[n](1\le c[i]\le 10^5) c[1],c[2],...,c[n]1c[i]105)

输出 m m m行,每行有一个整数。第 i i i行应当含有权值恰为 i i i神犇二叉树的总数。请输出答案关于 998244353 ( = 7 × 17 × 2 23 + 1 = 7 × 17 × 223 + 1 998244353(=7\times 17\times 2^{23}+1=7×17×223+1 998244353(=7×17×223+1=7×17×223+1,一个质数)取模后的结果。

输入输出样例

输入样例#1:
2 3
1 2
输出样例#1:
1
3
9
输入样例#2:
3 10
9 4 3
输出样例#2:
0
0
1
1
0
2
4
2
6
15
输入样例#3:
5 10
13 10 6 4 15
输出样例#3:
0
0
0
1
0
1
0
2
0
5

解题分析

考虑每个单独的点的生成函数, 显然为 r ( x ) = ∑ i ∈ S x i r(x)=\sum_{i\in S}x^i r(x)=iSxi

由于二叉树的结构可以递归形成, 那么对于一个子树的根节点, 设其生成函数为 F ( x ) F(x) F(x), 那么显然就有 F ( x ) = r ( x ) F 2 ( x ) + 1 F(x)=r(x)F^2(x)+1 F(x)=r(x)F2(x)+1(空树的情况要单独算, 不能放进生成函数里, 具体大家可以推几步就知道了)。

由求根公式可得 F ( x ) = 1 ± 1 − 4 r ( x ) 2 r ( x ) F(x)=\frac{1\pm\sqrt{1-4r(x)}}{2r(x)} F(x)=2r(x)1±14r(x)

先不论上面的正负号怎么取, 下面的 2 r ( x ) 2r(x) 2r(x)显然是没有 0 0 0次项, 也就无法求逆。

然后有个显然的转化: 上下同乘 1 ∓ 1 − 4 r ( x ) 1\mp \sqrt{1-4r(x)} 114r(x) ,可得 F ( x ) = 2 1 ∓ 1 − 4 r ( x ) F(x)=\frac{2}{1\mp\sqrt{1-4r(x)}} F(x)=114r(x) 2,那么如果取到负号还是没有零次项, 并且 F ( x ) F(x) F(x) 0 0 0次项系数应该为 1 1 1, 不满足这个条件。所以最后的解为 2 1 + 1 − 4 r ( x ) \frac{2}{1+\sqrt{1-4r(x)}} 1+14r(x) 2

接下来考虑如何开根。设 F ( x ) = A ( x ) F(x)=\sqrt{A(x)} F(x)=A(x) 还是那个牛顿迭代的套路:
F ( x ) = F 0 ( x ) − G ( F 0 ( x ) ) G ′ ( F 0 ( x ) ) F(x)=F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))} F(x)=F0(x)G(F0(x))G(F0(x))
在这里 G ( F ( x ) ) = F 2 ( x ) − A ( x ) G(F(x))=F^2(x)-A(x) G(F(x))=F2(x)A(x), 那么就有 F ( x ) = F 0 ( x ) − F 0 2 ( x ) − A ( x ) 2 F 0 ( x ) F(x)=F_0(x)-\frac{F_0^2(x)-A(x)}{2F_0(x)} F(x)=F0(x)2F0(x)F02(x)A(x)

递归处理即可。

代码如下:

#include <cstdio>
#include <cmath>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 400500
#define ll long long
#define G 3
#define Ginv 332748118
#define MOD 998244353
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
IN int fpow(R int base, R int tim)
{
	int ret = 1;
	W (tim)
	{
		if (tim & 1) ret = 1ll * ret * base % MOD;
		base = 1ll * base * base % MOD, tim >>= 1;
	}
	return ret;
}
int n, m;
int a1[MX], a2[MX], b[MX], c[MX], d[MX], e[MX], f[MX], g[MX];
int r[MX], rev[MX], ans[MX];
namespace Poly
{
	IN void NTT(int *dat, R int len, R bool typ)
	{
		for (R int i = 1; i < len; ++i) if (rev[i] > i) std::swap(dat[rev[i]], dat[i]);
		R int seg, step, bd, cur, now, buf1, buf2, deal, base;
		for (seg = 1; seg < len; seg <<= 1)
		{
			base = fpow(typ ? G : Ginv, (MOD - 1) / (seg << 1)), step = seg << 1;
			for (now = 0; now < len; now += step)
			{
				deal = 1, bd = now + seg;
				for (cur = now; cur < bd; ++cur, deal = 1ll * deal * base % MOD)
				{
					buf1 = dat[cur], buf2 = 1ll * dat[cur + seg] * deal % MOD;
					dat[cur] = (buf1 + buf2) % MOD, dat[cur + seg] = (buf1 - buf2 + MOD) % MOD;
				}
			}
		}
		if (typ) return; int inv = fpow(len, MOD - 2);
		for (R int i = 0; i < len; ++i) dat[i] = 1ll * dat[i] * inv % MOD;
	}
	void Getinv(R int up, R int lg, int *ind, int *ans)
	{
		if (up == 1) return ans[0] = fpow(ind[0], MOD - 2), void();
		Getinv(up >> 1, lg - 1, ind, ans);
		int len = up << 1, half = up >> 1; lg++;
		for (R int i = 1; i < len; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lg - 1);
		for (R int i = 0; i < len; ++i) a1[i] = a2[i] = 0;
		for (R int i = 0; i < half; ++i) a1[i] = ans[i];
		for (R int i = 0; i < up; ++i) a2[i] = ind[i];
		NTT(a1, len, 1), NTT(a2, len, 1);
		for (R int i = 0; i < len; ++i) a1[i] = (a1[i] * 2 % MOD - 1ll * a1[i] * a1[i] % MOD * a2[i] % MOD + MOD) % MOD;
		NTT(a1, len, 0);
		for (R int i = 0; i < up; ++i) ans[i] = a1[i];
	}
	IN void Getrt(R int up, R int lg, int *ind, int *ans)
	{
		if (up == 1) return ans[0] = 1, void();
		Getrt(up >> 1, lg - 1, ind, ans);
		int len = up << 1, half = up >> 1; lg++;
		for (R int i = 0; i < half; ++i) c[i] = ans[i] * 2 % MOD;
		Getinv(up, lg - 1, c, b);
		for (R int i = 1; i < len; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lg - 1);
		for (R int i = 0; i < len; ++i) a2[i] = 0;
		for (R int i = half; i < len; ++i) a1[i] = 0;
		for (R int i = 0; i < half; ++i) a1[i] = ans[i];
		NTT(a1, len, 1);
		for (R int i = 0; i < len; ++i) a1[i] = 1ll * a1[i] * a1[i] % MOD;
		NTT(a1, len, 0);
		for (R int i = up - 1; i < len; ++i) a1[i] = 0;
		for (R int i = 0; i < up; ++i) a1[i] = (a1[i] + ind[i]) % MOD, a2[i] = b[i];
		NTT(a1, len, 1), NTT(a2, len, 1);
		for (R int i = 0; i < len; ++i) a1[i] = 1ll * a1[i] * a2[i] % MOD;
		NTT(a1, len, 0);
		for (R int i = 0; i < up; ++i) ans[i] = a1[i];
	}
	IN void solve(R int up, R int lg)
	{
		for (R int i = 0; i < up; ++i) d[i] = (MOD - 2 * r[i] % MOD * 2 % MOD) % MOD;
		d[0] = (d[0] + 1) % MOD; Getrt(up, lg, d, e); e[0] = (e[0] + 1) % MOD;
		Getinv(up, lg, e, f);
		for (R int i = 0; i < up; ++i) ans[i] = f[i] * 2 % MOD;
	}
}
int main(void)
{
	in(n), in(m); int foo, len = 1, lg = 0;
	for (R int i = 1; i <= n; ++i) in(foo), r[foo] = 1;
	for (; len <= m; len <<= 1, lg++);
	Poly::solve(len, lg);
	for (R int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值