[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\)分别 ...
随机推荐
- 【Islands and Bridges】题解
题目 题目描述 给定一些岛屿和一些连接岛屿的桥梁,大家都知道汉密尔顿路是访问每个岛屿一次的路线,在我们这个地图中,每个岛屿有个正整数的权值,表示这个岛屿的观赏价值.假设一共有N个岛屿,用Vi表示岛屿C ...
- (转载)自然语言处理中的Attention Model:是什么及为什么
转载说明来源:http://blog.csdn.net/malefactor/article/details/50550211 author: 张俊林 原文写得非常好! 原文: 要是关注深度学习在自然 ...
- CQOI2010 传送带
题目链接:戳我 分别枚举线段AB上的出发点,和线段CD上的到达点,然后时间直接计算,取min就可以了. 但是这样子显然会T飞,(相当于1e5的平方吧?)所以我们进一步考虑性质. 然后打表(或者感性理解 ...
- 容器适配器————priority_queue
#include <queue> priority_queue 容器适配器定义了一个元素有序排列的队列.默认队列头部的元素优先级最高.因为它是一个队列,所以只能访问第一个元素,这也意味着优 ...
- JS深度判断两个数组对象字段相同
/** * 判断此对象是否是Object类型 * @param {Object} obj */ function isObject(obj){ return Object.prototype.toSt ...
- Elasticsear搭建
2.1:创建用户: (elasticsearch不能使用root用户) useradd angelpasswd angel 2.2:解压安装包 tar -zxvf elasticsearch-5.5. ...
- 关于判断StringBuffer是否为空
对于String和StringBuffer来说,都是通过创建新的char value[]数组来达到字符串改变的操作的,只不过String是通过新创建String对象来达到目的, 而StringBuff ...
- DjangoRestFrameWork 版本控制
DRF的版本控制 为什么需要版本控制 API 版本控制允许我们在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据). DRF提供了许多不同的版本控制方案. 可能会有一些客户端因为某些原因 ...
- java.sql.SQLException: The server time zone value 'Öùú±ê׼ʱ¼ä' is unrecognized or repr
在数据库连接配置文件中加入以下: 解决办法为在application文件中添加serverTimezone=UTC spring.datasource.url=jdbc:mysql://localho ...
- Spring下面的@Transactional注解的讲解
摘自: https://www.cnblogs.com/xiohao/p/4808088.html Spring下面的@Transactional注解标志的讲解 最近在开发中对Spring中的事务标记 ...