[codeforces438E]The Child and Binary Tree

试题描述

Our child likes computer science very much, especially he likes binary trees.

Consider the sequence of n distinct positive integers: \(c_1, c_2, \cdots , c_n\). The child calls a vertex-weighted rooted binary tree good if and only if for every vertex v, the weight of v is in the set \(\{c_1, c_2, \cdots , c_n\}\). Also our child thinks that the weight of a vertex-weighted tree is the sum of all vertices' weights.

Given an integer \(m\), can you for all \(s (1 \le s \le m)\) calculate the number of good vertex-weighted rooted binary trees with weight \(s\)? Please, check the samples for better understanding what trees are considered different.

We only want to know the answer modulo \(998244353\) (\(7 \times 17 \times 223 + 1\), a prime number).

给出 \(n\) 种点的点权,定义一棵二叉树的权值等于它所有点的点权和。求对于 \([1, m]\) 中的 \(s\),权值为 \(s\) 的不同的二叉树有多少种。两棵二叉树不同当且仅当它们的左子树、右子树或根节点点权不同。一棵二叉树中可以出现多个点权相同的点。

输入

The first line contains two integers \(n, m (1 \le n \le 10^5; 1 \le m \le 10^5)\). The second line contains n space-separated pairwise distinct integers \(c_1, c_2, ..., c_n\). \((1 \le c_i \le 10^5)\).

输出

Print \(m\) lines, each line containing a single integer. The \(i\)-th line must contain the number of good vertex-weighted rooted binary trees whose weight exactly equal to \(i\). Print the answers modulo \(998244353\) (\(7 \times 17 \times 2^{23} + 1\), a prime number).

输入示例

3 10
9 4 3

输出示例

0
0
1
1
0
2
4
2
6
15

数据规模及约定

见“输入

题解

首先看看暴力 dp 怎么解决这个问题。设 \(f_k\) 表示权值为 \(k\) 的二叉树的数目,那么有转移方程(注意 dp 边界):

\[f_k = \sum_{i=1}^n { \sum_{j=0}^{k-c_i} f_{k-j-c_i} \cdot f_j } \\\\
f_0 = 1
\]

然后搞生成函数,令 \(C(x) = \sum_{i=1}^n { x^{c_i} }\),\(F(x) = \sum_{i=0}^{+ \infty} { f_i \cdot x^i }\)。

然后我们发现里面的 sigma 是一个卷积,然后把式子缩一点:

\[[x^k]F(x) = \sum_{i=1}^n { 1 \cdot [x^{k-c_i}]F^2(x) } \\\\
[x^0]F(x) = 1
\]

然后前面那个 \(1\),由于只在幂是 \(c_i\) 的时候出现,可以想象 \(C(x)\) 又在和 \(F^2(x)\) 做卷积,即

\[[x^k]F(x) = \sum_{i=1}^n { [x^{c_i}]C(x) \cdot [x^{k-c_i}]F^2(x) } \\\\
[x^0]F(x) = 1
\]

然后我们发现可以化成初中学过的二元一次方程的形式:

\[F(x) = C(x)F^2(x) + 1
\]

用 \(x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}\) 求根公式解一下上面这个关于 \(F(x)\) 的方程,得到

\[F(x) = \frac{1 \pm \sqrt{1-4C(x)}}{2C(x)}
\]

两个解,怎么办呢?

初中老师告诉我们:检验!

怎么检验?我们从 \([x^0]F(x) = 1\) 入手,可以发现这就是在 \(x = 0\) 的时候,\(F(x) = 1\)。

但是由于 \(C(x)\) 常数项为 \(0\),且它在分母,所以显然有

\[\lim_{x \rightarrow 0} \frac{1 + \sqrt{1-4C(x)}}{2C(x)} = + \infty
\]

所以可以排除这个解了,但为了严谨,我们当然还要验证一下另一个解,但是另一个解的检验比较棘手,因为我们会得到一个 \(0\) 除以 \(0\) 的形式,这时候就需要用洛必达法则了(\(\leftarrow\) 戳它进入百度百科)

\[\lim_{x \rightarrow 0} \frac{1 - \sqrt{1-4C(x)}}{2C(x)} \\\\
= \lim_{x \rightarrow 0} \frac{1 - \sqrt{1-4x}}{2x} \\\\
= \lim_{x \rightarrow 0} \frac{\frac{\mathrm{d}(1 - \sqrt{1-4x})}{\mathrm{d}x}}{\frac{\mathrm{d}(2x)}{\mathrm{d}x}} \\\\
= 1
\]

(以上直接跳过求导过程,读者不妨仔细手算一下)正确了!

那么接下来搞一个多项式求逆、开方\(^*\)就好啦。

注意,上面的式子不能直接算,因为 \(C(x)\) 常数项为 \(0\),不存在逆元!不过没关系,我们可以分子有理化一下:

\[\frac{1 - \sqrt{1-4C(x)}}{2C(x)} \\
= \frac{1 - (1 - 4C(x))}{2C(x) (1 + \sqrt{1 - 4C(x)})} \\
= \frac{2}{1 + \sqrt{1 - 4C(x)}}
\]

这样就可以直接求逆元啦!

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--) const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
int read() {
int x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
} #define maxn 524288
#define MOD 998244353
#define Groot 3
#define LL long long int Pow(int a, int b) {
int ans = 1, t = a;
while(b) {
if(b & 1) ans = (LL)ans * t % MOD;
t = (LL)t * t % MOD; b >>= 1;
}
return ans;
} int brev[maxn];
void FFT(int *a, int len, int tp) {
int n = 1 << len;
rep(i, 0, n - 1) if(i < brev[i]) swap(a[i], a[brev[i]]);
rep(i, 1, len) {
int wn = Pow(Groot, MOD - 1 >> i);
if(tp < 0) wn = Pow(wn, MOD - 2);
for(int j = 0; j < n; j += 1 << i) {
int w = 1;
rep(k, 0, (1 << i >> 1) - 1) {
int la = a[j+k], ra = (LL)w * a[j+k+(1<<i>>1)] % MOD;
a[j+k] = (la + ra) % MOD;
a[j+k+(1<<i>>1)] = (la - ra + MOD) % MOD;
w = (LL)w * wn % MOD;
}
}
}
if(tp < 0) rep(i, 0, n - 1) a[i] = (LL)a[i] * Pow(n, MOD - 2) % MOD;
return ;
}
void Mul(int *A, int an, int *B, int bn) {
int n = an + bn, N = 1, len = 0;
while(N <= n) N <<= 1, len++;
rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len - 1);
FFT(A, len, 1); FFT(B, len, 1);
rep(i, 0, N - 1) A[i] = (LL)A[i] * B[i] % MOD;
FFT(A, len, -1);
return ;
} int tmp[maxn];
void inverse(int *f, int *g, int n) { // module x^n
if(n == 1) return (void)(f[0] = Pow(g[0], MOD - 2));
inverse(f, g, n + 1 >> 1);
int N = 1, len = 0;
while(N <= (n << 2)) N <<= 1, len++;
rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len - 1);
rep(i, n + 1 >> 1, N - 1) f[i] = 0; rep(i, 0, n - 1) tmp[i] = g[i]; rep(i, n, N - 1) tmp[i] = 0;
FFT(f, len, 1); FFT(tmp, len, 1);
rep(i, 0, N - 1) f[i] = (2ll - (LL)tmp[i] * f[i] % MOD + MOD) * f[i] % MOD;
FFT(f, len, -1);
rep(i, n, N - 1) f[i] = 0;
return ;
} int inv[maxn], _inv[maxn];
void p_sqrt(int *f, int *g, int n) { // g[0] = 1
if(n == 1) return (void)(f[0] = 1);
p_sqrt(f, g, n + 1 >> 1);
rep(i, 0, (n + 1 >> 1) - 1) _inv[i] = (f[i] << 1) % MOD; rep(i, n + 1 >> 1, n - 1) _inv[i] = 0;
inverse(inv, _inv, n);
int N = 1, len = 0;
while(N <= n + 1) N <<= 1, len++;
rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len - 1);
rep(i, n + 1 >> 1, N - 1) f[i] = 0; rep(i, 0, n - 1) tmp[i] = g[i]; rep(i, n, N - 1) tmp[i] = 0;
FFT(f, len, 1); FFT(tmp, len, 1);
rep(i, 0, N - 1) f[i] = (tmp[i] + (LL)f[i] * f[i]) % MOD;
FFT(f, len, -1);
rep(i, n, N - 1) f[i] = 0;
Mul(f, n - 1, inv, n - 1);
N = 1; while(N <= (n - 1 << 1)) N <<= 1;
rep(i, n, N - 1) f[i] = 0;
return ;
} int num[50], cntn;
void putint(int x) {
if(!x) return (void)puts("0");
cntn = 0;
while(x) num[++cntn] = x % 10, x /= 10;
dwn(i, cntn, 1) putchar(num[i] + '0'); putchar('\n');
return ;
} int n, val[maxn], C[maxn], c[maxn];
int main() {
int n = read(), m = read(), mxv = 0;
rep(i, 1, n) val[i] = read(), mxv = max(mxv, val[i]); rep(i, 1, n) if(val[i] <= m) C[val[i]]++;
rep(i, 1, m) C[i] = MOD - 4ll * C[i] % MOD; C[0] = 1;
p_sqrt(c, C, m + 1); (c[0] += 1) %= MOD;
inverse(C, c, m + 1);
rep(i, 0, m) (C[i] <<= 1) %= MOD; rep(i, 1, m) putint(C[i]); return 0;
}

\(^*\)多项式开方,可以用前文(请翻到最后一题)的方法,但要用到多项式求 ln 和 exp,很麻烦,常数也大。这里我们可以利用一下开根的次数为 \(2\) 这个特殊性质优化一下。(以下多项式都省略后面的 \((x)\))

还是考虑倍增,令 \(f_0^2 \equiv g (\mathrm{mod}\ x^{\lceil \frac{n}{2} \rceil})\),用 \(f_0, g\) 表示出 \(f^2 \equiv g (\mathrm{mod}\ x^n)\) 的 \(f\)。

显然有

\[f - f_0 \equiv 0 (\mathrm{mod}\ x^{\lceil \frac{n}{2} \rceil})
\]

两边平方一下就好啦

\[(f - f_0)^2 \equiv 0 (\mathrm{mod}\ x^n) \\\\
f^2 - 2f_0f + f_0^2 \equiv 0 (\mathrm{mod}\ x^n) \\\\
g - 2f_0f + f_0^2 \equiv 0 (\mathrm{mod}\ x^n)
\]

于是得到

\[f \equiv \frac{g + f_0^2}{2f_0} (\mathrm{mod}\ x^n)
\]

[codeforces438E]The Child and Binary Tree的更多相关文章

  1. Codeforces 250 E. The Child and Binary Tree [多项式开根 生成函数]

    CF Round250 E. The Child and Binary Tree 题意:n种权值集合C, 求点权值和为1...m的二叉树的个数, 形态不同的二叉树不同. 也就是说:不带标号,孩子有序 ...

  2. 【CF438E】The Child and Binary Tree(多项式运算,生成函数)

    [CF438E]The Child and Binary Tree(多项式运算,生成函数) 题面 有一个大小为\(n\)的集合\(S\) 问所有点权都在集合中,并且点权之和分别为\([0,m]\)的二 ...

  3. [题解] CF438E The Child and Binary Tree

    CF438E The Child and Binary Tree Description 给一个大小为\(n\)的序列\(C\),保证\(C\)中每个元素各不相同,现在你要统计点权全在\(C\)中,且 ...

  4. 【CF】438E. The Child and Binary Tree

    http://codeforces.com/contest/438/problem/E 题意:询问每个点权值在 $c_1, c_2, ..., c_m$ 中,总权值和为 $s$ 的二叉树个数.请给出每 ...

  5. Codeforces 438E. The Child and Binary Tree 多项式,FFT

    原文链接www.cnblogs.com/zhouzhendong/p/CF438E.html 前言 没做过多项式题,来一道入门题试试刀. 题解 设 $a_i$ 表示节点权值和为 $i$ 的二叉树个数, ...

  6. CF438E The Child and Binary Tree 生成函数、多项式开根

    传送门 设生成函数\(C(x) = \sum\limits_{i=0}^\infty [\exists c_j = i]x^i\),答案数组为\(f_1 , f_2 , ..., f_m\),\(F( ...

  7. cf438E. The Child and Binary Tree(生成函数 多项式开根 多项式求逆)

    题意 链接 Sol 生成函数博大精深Orz 我们设\(f(i)\)表示权值为\(i\)的二叉树数量,转移的时候可以枚举一下根节点 \(f(n) = \sum_{w \in C_1 \dots C_n} ...

  8. Codeforces 438E The Child and Binary Tree [DP,生成函数,NTT]

    洛谷 Codeforces 思路 看到计数和\(998244353\),可以感觉到这是一个DP+生成函数+NTT的题. 设\(s_i\)表示\(i\)是否在集合中,\(A\)为\(s\)的生成函数,即 ...

  9. Codeforces 438E The Child and Binary Tree - 生成函数 - 多项式

    题目传送门 传送点I 传送点II 传送点III 题目大意 每个点的权值$c\in {c_{1}, c_{2}, \cdots, c_{n}}$,问对于每个$1\leqslant s\leqslant ...

随机推荐

  1. DateTools,可能是最好用的iOS日期工具库

    项目简介 DateTools 用于提高Objective-C中日期和时间相关操作的效率.灵感来源于 DateTime和Time Period Library. 项目主页: DateTools 最新示例 ...

  2. JavaScript的算术、赋值、关系运算符的讲解

    JS中的运算符分为:算术/赋值/关系/逻辑/字符串       算术运算符:  +加法    -减法    *乘法    /除法    %取余 var a = 1, b = 2; a + b = 3 ...

  3. OCCI线程安全

    线程是任务调度的基本单位,一个进程中可以有多个线程,每个线程有自己的堆栈空间, 进程中的代码段.数据段和堆栈对进程中的线程是可见的.在使用线程时通常都要考虑数据的安全访问. 常用的线程同步方法有: 互 ...

  4. Hibernate进阶学习3

    Hibernate进阶学习3 测试hibernate的多表关联操作(一对多,多对一,多对多) 表之间的关系主要在类与元数据配置文件中体现 package com.hibernate.domain; i ...

  5. B-树 分合之道

    P.s:在代码里会同时用到向量和B-树的search,insert, remove,具体调用的是哪个结构的函数结合上下文就能看懂. 根据上一篇文章,我们对于这棵树的大致结构已经明了,那该如何有效利用并 ...

  6. 笔记-python-多环境-virtualenv

    笔记-python-多环境-virtualenv 1.      多环境 在开发Python应用程序的时候,系统安装的Python3只有一个版本:3.6.4,所有第三方的包都会被pip安装到Pytho ...

  7. 笔记-python-lib-lxml

    笔记-python-lib-lxml 1.      lxml简介 lxml是一个实现解析网页文件的库,python中自带有解析库,但没有lxml方便好用. The lxml XML toolkit ...

  8. 1 web应用

    web应用 Web应用程序是一种可以通过Web访问的应用程序,程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件.应用程序有两种模式C/S.B/S.C/S是客户端/服 ...

  9. 设计模式--单例模式Singleton

    单例模式顾名思义整个程序下只有一个实例,例如一个国家只有一个皇帝,一个军队只有一个将军.单例模式的书写又分为饿汉模式和懒汉模式 饿汉模式   类中代码 package demo; public cla ...

  10. 全方位认识HDMI接口技术

    HDMI接口并不是一个开放的标准.制造商必须向HDMI标准制定协会支付版税,来换取一个生产许可证.不过这个版税可不便宜,每年要交纳15000美元的许可费,并且更黑的是每生产一个HDMI接口就要支付0. ...