[BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆)
[BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆)
题面
一棵二叉树的所有点的点权都是给定的集合中的一个数。
让你求出1到m中所有权值为i的二叉树的个数。
两棵树不同当且仅当树的形态不一样或者是树的某个点的点权不一样
分析
设\(c(i)\)表示数值i是否在集合中。\(f(i)\)表示权值为i的二叉树的个数。那么
\]
其中\(i\)表示根节点的权值,那么左右子树的权值和为\(n-i\),枚举左右子树分别的权值\(j,n-i-j\),为\(\sum_{j=0}^{n-i} f(j)f(n-i-j)\)
我们把式子化成卷积的形式,设\(F,C\)为\(f,c\)的生成函数
\(F(x)=F(x)^2C(x)+1\)
解函数方程,得:
\]
如果符号取-,那么x=0时分母为0无意义。
因此\(F(x)=\frac{2}{1+\sqrt{1-4C(x)}}\)
多项式开方和多项式求逆即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 400000
#define G 3
#define invG 332748118
#define inv2 499122177
#define mod 998244353
using namespace std;
typedef long long ll;
inline ll fast_pow(ll x,ll k){
ll ans=1;
while(k){
if(k&1) ans=ans*x%mod;
x=x*x%mod;
k>>=1;
}
return ans;
}
inline ll inv(ll x){
return fast_pow(x,mod-2);
}
void NTT(ll *x,int n,int type){
static int rev[maxn+5];
int tn=1,k=0;
while(tn<n){
tn*=2;
k++;
}
for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
for(int i=0;i<n;i++) if(i<rev[i]) swap(x[i],x[rev[i]]);
for(int len=1;len<n;len*=2){
int sz=len*2;
ll gn1=fast_pow((type==1?G:invG),(mod-1)/sz);
for(int l=0;l<n;l+=sz){
int r=l+len-1;
ll gnk=1;
for(int i=l;i<=r;i++){
ll tmp=x[i+len];
x[i+len]=(x[i]-gnk*tmp%mod+mod)%mod;
x[i]=(x[i]+gnk*tmp%mod)%mod;
gnk=gnk*gn1%mod;
}
}
}
if(type==-1){
ll invn=inv(n);
for(int i=0;i<n;i++) x[i]=x[i]*invn%mod;
}
}
void mul(ll *a,ll *b,ll *ans,int n){
NTT(a,n,1);
NTT(b,n,1);
for(int i=0;i<n;i++) ans[i]=a[i]*b[i]%mod;
NTT(ans,n,-1);
}
void get_inv(ll *a,ll *b,int n){
static ll tmpa[maxn+5],tmpb[maxn+5];
b[0]=inv(a[0]);
int len;
for(len=1;len<n*2;len*=2){
int sz=len*2;
for(int i=0;i<len;i++){
tmpa[i]=a[i];
tmpb[i]=b[i];
}
NTT(tmpa,sz,1);
NTT(tmpb,sz,1);
for(int i=0;i<sz;i++) b[i]=tmpb[i]*(2-tmpb[i]*tmpa[i]%mod+mod)%mod;
NTT(b,sz,-1);
for(int i=len;i<sz;i++) b[i]=0;
}
for(int i=0;i<len;i++) tmpa[i]=tmpb[i]=0;
for(int i=n;i<len;i++) b[i]=0;
}
void get_sqrt(ll *a,ll *b,int n){
static ll tmpa[maxn+5],invb[maxn+5];
b[0]=1;
int len;
for(len=1;len<n*2;len*=2){
int sz=len*2;
for(int i=0;i<len;i++) tmpa[i]=a[i];
get_inv(b,invb,len);
mul(tmpa,invb,tmpa,sz);
for(int i=0;i<len;i++) b[i]=inv2*(tmpa[i]+b[i])%mod;
for(int i=len;i<sz;i++) b[i]=0;
}
for(int i=0;i<len;i++) tmpa[i]=invb[i]=0;
for(int i=n;i<len;i++) b[i]=0;
}
int n,m;
ll c[maxn+5],sqtc[maxn+5],isqtc[maxn+5];
int main(){
int x;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&x);
c[x]++;
}
int dn=1;
while(dn<=m) dn*=2;
for(int i=1;i<dn;i++) c[i]=((-4)*c[i]+mod)%mod;
c[0]++;//sqrt(1-4C)
get_sqrt(c,sqtc,dn);
sqtc[0]++;//1+sqrt(1-4C)
get_inv(sqtc,isqtc,dn);
for(int i=0;i<=m;i++) isqtc[i]=isqtc[i]*2%mod;//2/(1+sqrt(1-4C)
for(int i=1;i<=m;i++) printf("%lld\n",isqtc[i]);
}
[BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆)的更多相关文章
- BZOJ #3625 CF #438E 小朋友和二叉树
清真多项式题 BZOJ #3625 codeforces #438E 题意 每个点的权值可以在集合$ S$中任取 求点权和恰好为$[1..n]$的不同的二叉树数量 数据范围全是$ 10^5$ $ So ...
- [BZOJ3625][CF438E]小朋友和二叉树 (多项式开根,求逆)
题面 题解 设多项式的第a项为权值和为a的二叉树个数,多项式的第a项表示是否为真,即 则,所以F是三个多项式的卷积,其中包括自己: ,1是F的常数项,即. 我们发现这是一个一元二次方程,可以求出,因为 ...
- 【BZOJ3625】【CF438E】小朋友和二叉树 NTT 生成函数 多项式开根 多项式求逆
题目大意 考虑一个含有\(n\)个互异正整数的序列\(c_1,c_2,\ldots ,c_n\).如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合\(\{c_1,c_2,\ldots ,c_n\ ...
- BZOJ 3625:小朋友和二叉树 多项式开根+多项式求逆+生成函数
生成函数这个东西太好用了~ code: #include <bits/stdc++.h> #define ll long long #define setIO(s) freopen(s&q ...
- 【BZOJ3625】【codeforces438E】小朋友和二叉树 生成函数+多项式求逆+多项式开根
首先,我们构造一个函数$G(x)$,若存在$k∈C$,则$[x^k]G(x)=1$. 不妨设$F(x)$为最终答案的生成函数,则$[x^n]F(x)$即为权值为$n$的神犇二叉树个数. 不难推导出,$ ...
- Codeforces 250 E. The Child and Binary Tree [多项式开根 生成函数]
CF Round250 E. The Child and Binary Tree 题意:n种权值集合C, 求点权值和为1...m的二叉树的个数, 形态不同的二叉树不同. 也就是说:不带标号,孩子有序 ...
- BZOJ3625 [Codeforces Round #250]小朋友和二叉树(生成函数+多项式开根)
设f(n)为权值为n的神犇二叉树个数.考虑如何递推求这个东西. 套路地枚举根节点的左右子树.则f(n)=Σf(i)f(n-i-cj),cj即根的权值.卷积的形式,cj也可以通过卷上一个多项式枚举.可以 ...
- [BZOJ3625][Codeforces Round #250]小朋友和二叉树 多项式开根+求逆
https://www.lydsy.com/JudgeOnline/problem.php?id=3625 愉快地列式子.设\(F[i]\)表示权值为\(i\) 的子树的方案数,\(A[i]\)为\( ...
- 【XSY2730】Ball 多项式exp 多项式ln 多项式开根 常系数线性递推 DP
题目大意 一行有\(n\)个球,现在将这些球分成\(k\) 组,每组可以有一个球或相邻两个球.一个球只能在至多一个组中(可以不在任何组中).求对于\(1\leq k\leq m\)的所有\(k\)分别 ...
随机推荐
- 将Emacs Org mode用于GTD任务管理
在上一篇日志中,我简要介绍了如何围绕Emacs Org mode构建个人任务管理系统的基本思路与方法.因为Org mode体系庞大.功能繁杂,本文仅以提纲契领的方式介绍不同环节在Org mode中的操 ...
- [Noip模拟题]宠物之战senso
Description 众所周知,moreD的宠物已经被moreD奴役得体无完肤.这只宠物实在忍无可忍,把自己每天走魔法树的经历告诉了 自己的宠物.同时他还说明了自己爬树是多么地慢,以至于moreD每 ...
- Shell中Bash的基本功能(二)
1 历史命令 1)历史命令的查看[root@localhost ~]# history [选项] [历史命令保存文件]选项:-c: 清空历史命令-w: 把缓存中的历史命令写入历史命令保存文件.如果不手 ...
- scrapy项目1:爬取某培训机构老师信息(spider类)
1.scrapy爬虫的流程,可简单该括为以下4步: 1).新建项目---->scrapy startproject 项目名称(例如:myspider) >>scrapy.cfg为项目 ...
- hihoCoder #1558 : H国的身份证号码I
题目链接:https://hihocoder.com/problemset/problem/1558 H国的身份证号码I 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 ...
- 时间戳Unix和时间之间的转换
时间戳 ---> 时间 var a="1523258178"; var start = new Date(a).format("yyyy-MM-dd hh:mm ...
- python之random随机函数
random.random()用于生成 用于生成一个指定范围内的随机符点数,两个参数其中一个是上限,一个是下限.如果a > b,则生成随机数 1 n: a <= n <= b.如果 ...
- 关闭layer.open打开的页面
window.parent.location.reload(); //刷新父页面 var index = parent.layer.getFrameIndex(window.name); //获取窗口 ...
- mysql补0操作有什么意义?
比如我们在创建int的时候会使用int(10)这样的方式来定义某一个列,但是这样定义是没有任何意义的. Create Table showzerofill(Val1 INT(5) ZEROFILL, ...
- RedHat系统文本界面安装图形界面方法
版本: Linux version 2.6.32-431.el6.x86_64 (mockbuild@x86-023.build.eng.bos.redhat.com) (gcc version 4. ...