【CF438E】小朋友和二叉树 解题报告
【CF438E】小朋友和二叉树
Description
我们的小朋友很喜欢计算机科学,而且尤其喜欢二叉树。
考虑一个含有\(n\)个互异正整数的序列\(c_1,c_2,\dots,c_n\)。如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合\(\{c_1,c_2,\dots,c_n\}\)中,我们的小朋友就会将其称作神犇的。并且他认为,一棵带点权的树的权值,是其所有顶点权值的总和。
给出一个整数\(m\),你能对于任意的\(s(1≤s≤m)\)计算出权值为\(s\)的神犇二叉树的个数吗?请参照样例以更好的理解什么样的两棵二叉树会被视为不同的。
我们只需要知道答案关于\(998244353\)取模后的值。
Input
第一行有\(2\)个整数 \(n,m(1≤n≤10^5,1≤m≤10^5)\).
第二行有\(n\)个用空格隔开的互异的整数 \(c_1,c_2,…,c_n(1≤c[i]≤10^5)\).
Output
输出\(m\)行,每行有一个整数。第\(i\)行应当含有权值恰为\(i\)的神犇二叉树的总数。请输出答案关于\(998244353\)取模后的结果。
考虑\(DP\),设\(f_i\)代表权值为\(i\)的二叉树的个数,\(C_i\)代表是否存在权值为\(i\)的节点
\]
\]
然后我们发现长得很像卷积,但是没法好好卷自己。
于是构造一波生成函数,直接表示为系数就行了
\]
关于这个\(+1\),我的理解是,加了一个常数项为\(1\)的多项式表示\(f_0=1\)
然后解一下二次方程,得到
\]
然后讨论一下发现需要取+
再次化简
\]
套用多项式求逆+开根就可以了
Code:
#include <cstdio>
#include <cctype>
#include <algorithm>
const int N=(1<<20)+10;
const int mod=998244353,Gi=332748118,i2=499122177;
int read()
{
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) {x=x*10+c-'0';c=getchar();}
return x;
}
#define add(x,y) ((x+y)%mod)
#define mul(x,y) (1ll*(x)*(y)%mod)
int qp(int d,int k){int f=1;while(k){if(k&1)f=mul(f,d);d=mul(d,d),k>>=1;}return f;}
int C[N],sq[2][N],b[2][N],A[N],B[N],turn[N];
void NTT(int *a,int len,int typ)
{
for(int i=1;i<len;i++) if(i<turn[i]) std::swap(a[i],a[turn[i]]);
for(int le=1;le<len;le<<=1)
{
int wn=qp(typ?3:Gi,(mod-1)/(le<<1));
for(int p=0;p<len;p+=le<<1)
{
int w=1;
for(int i=p;i<p+le;i++,w=mul(w,wn))
{
int tx=a[i],ty=mul(w,a[i+le]);
a[i]=add(tx,ty);
a[i+le]=add(tx,mod-ty);
}
}
}
if(!typ)
{
int inv=qp(len,mod-2);
for(int i=0;i<len;i++) a[i]=mul(a[i],inv);
}
}
void polymul(int *a,int *b,int len)
{
int L=-1;for(int i=1;i<len;i<<=1) ++L;
for(int i=0;i<len;i++) turn[i]=turn[i>>1]>>1|(i&1)<<L,A[i]=B[i]=0;
for(int i=0;i<len>>1;i++) A[i]=a[i],B[i]=b[i];
NTT(A,len,1),NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=mul(A[i],B[i]);
NTT(A,len,0);
for(int i=0;i<len;i++) a[i]=A[i];
}
void polyinv(int *a,int n)
{
int len=2,cur=0;
b[cur][0]=qp(a[0],mod-2);
while(len<=(n<<2))
{
cur^=1;
for(int i=0;i<len>>1;i++) b[cur][i]=add(b[cur^1][i],b[cur^1][i]);
polymul(b[cur^1],b[cur^1],len);
polymul(b[cur^1],a,len);
for(int i=0;i<len;i++) b[cur][i]=add(b[cur][i],mod-b[cur^1][i]);
len<<=1;
}
for(int i=0;i<n;i++) a[i]=b[cur][i];
}
void polysqrt(int *a,int n)
{
int len=2,cur=0;
sq[cur][0]=1;
while(len<=(n<<2))
{
cur^=1;
for(int i=0;i<len>>1;i++) sq[cur][i]=mul(sq[cur^1][i],i2);
for(int i=0;i<len>>1;i++) sq[cur^1][i]=add(sq[cur^1][i],sq[cur^1][i]);
polyinv(sq[cur^1],len);
polymul(sq[cur^1],a,len);
for(int i=0;i<len;i++) sq[cur][i]=add(sq[cur][i],sq[cur^1][i]);
len<<=1;
}
for(int i=0;i<n;i++) a[i]=sq[cur][i];
}
int main()
{
int n=read(),m=read();
for(int i=1;i<=n;i++) C[read()]=1;
++m;C[0]=1;
for(int i=1;i<m;i++) C[i]=(mod-(C[i]<<2))%mod;
polysqrt(C,m);
C[0]=add(C[0],1);
polyinv(C,m);
for(int i=0;i<m;i++) C[i]=add(C[i],C[i]);
for(int i=1;i<m;i++) printf("%d\n",C[i]);
return 0;
}
2018.12.17
【CF438E】小朋友和二叉树 解题报告的更多相关文章
- 【九度OJ】题目1113:二叉树 解题报告
[九度OJ]题目1113:二叉树 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1113 题目描述: 如上所示,由正整数1,2,3-- ...
- 【剑指Offer】从上往下打印二叉树 解题报告(Python)
[剑指Offer]从上往下打印二叉树 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interviews ...
- 【剑指Offer】序列化二叉树 解题报告(Python)
[剑指Offer]序列化二叉树 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interviews 题目 ...
- 【剑指Offer】按之字形顺序打印二叉树 解题报告(Python)
[剑指Offer]按之字形顺序打印二叉树 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-intervie ...
- [BZOJ3625][CF438E]小朋友和二叉树
题面 Description 我们的小朋友很喜欢计算机科学,而且尤其喜欢二叉树. 考虑一个含有\(n\)个互异正整数的序列\(c_1,c_2,\ldots,c_n\).如果一棵带点权的有根二叉树满足其 ...
- 【剑指Offer】07. 重建二叉树 解题报告(Java & Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人微信公众号:负雪明烛 目录 题目描述 解题方法 基本方法:线性查找根节点的位置 方法优 ...
- [CF438E] 小朋友和二叉树
Description 给定一个整数集合 \(c\),对于每个 \(i\in[1,m]\),求有多少种不同的带点权的二叉树使得这棵树点权和为 \(i\) 并且顶点的点权全部在集合 \(c\) 中.\( ...
- [BZOJ3625][CF438E]小朋友和二叉树 (多项式开根,求逆)
题面 题解 设多项式的第a项为权值和为a的二叉树个数,多项式的第a项表示是否为真,即 则,所以F是三个多项式的卷积,其中包括自己: ,1是F的常数项,即. 我们发现这是一个一元二次方程,可以求出,因为 ...
- BZOJ3625 CF438E 小朋友与二叉树
心态崩了 不放传送门了 辣鸡bz 还是正经一点写一下题解= = 就是显然我们可以把权值写成生成函数形式g(0/1序列)来表示权值是否出现 然后f来表示总的方案数 可以列出 分别枚举左右子树和空树的情况 ...
随机推荐
- Java SE练习题——求奇数
欢迎来到Java SE练习题频道,我是Fishing,今天我带来的练习题是(做题会有不足之处,可评论,说出更好的方法): 通过键盘输入两个整数,计算这两个整数之间的所有奇数之和,并输出计算结果. 看到 ...
- 主成分分析——PCA
在数据挖掘过程中,当一个对象有多个属性(即该对象的测量过程产生多个变量)时,会产生高维度数据,这给数据挖掘工作带来了难度,我们希望用较少的变量来描述数据的绝大多数信息,此时一个比较好的方法是先对数据进 ...
- [zabbix] zabbix检测mysql主从状态
环境说明: zabbix-proxy 172.16.2.95(zabbix-server同理) zabbix-agent111 172.16.2.111 mysql从机 1.mysql从机添加用户权限 ...
- linux安装nginx并配置负载均衡
linux上安装nginx比较简单: 前提是需要有gcc或者g++ 1.yum需要的依赖 yum -y install openssl openssl-devel 2.解压pcre库.zlib库 ...
- PHP--面向对象的设计原则
三大特性是:封装.继承.多态 所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏. 封装是面向对象的特征之一,是对象和类概念的主要特 ...
- Ubuntu下开启mysql远程访问
1. 开启数据库3306端口 首先,使用如下指令查看3306端口是否对外开放. netstat -an | grep 3306 tcp 0 0 127.0.0.1:3306 0.0.0.0:* LIS ...
- 2018软工实践—Beta冲刺(2)
队名 火箭少男100 组长博客 林燊大哥 作业博客 Beta 冲鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调组内工作 修改前端界面 展示GitHub当日代码/文档签入记录(组内 ...
- vue 实战报错解决方案
最近做项目,遇到一个问题 列表滚动,上拉加载功能 采用了better-scroll 插件,将better-scroll 封装成组件,采用父组件传递值给子组件的方式,子组件 采用$emit 方式 通知父 ...
- Few-Shot/One-Shot Learning
Few-Shot/One-Shot Learning指的是小样本学习,目的是克服机器学习中训练模型需要海量数据的问题,期望通过少量数据即可获得足够的知识. Matching Networks for ...
- 通用的将Excel导入数据集的方法
http://blog.csdn.net/baronyang/article/details/7048563