【BZOJ3625】【CF438E】小朋友和二叉树 NTT 生成函数 多项式开根 多项式求逆
题目大意
考虑一个含有\(n\)个互异正整数的序列\(c_1,c_2,\ldots ,c_n\)。如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合\(\{c_1,c_2,\ldots ,c_n\}\)中,我们的小朋友就会将其称作神犇的。并且他认为,一棵带点权的树的权值,是其所有顶点权值的总和。
给出一个整数\(m\),你能对于任意的\(s(1\leq s\leq m)\)计算出权值为\(s\)的神犇二叉树的个数吗?
我们只需要知道答案关于\(998244353\)取模后的值。
\(n,m\leq 100000\)
题解
设\(a_i\)为\(c\)中有没有\(i\)这个权值。
设\(f_i\)为权值和为\(i\)的二叉树个数。
f_0&=1\\
f_i&=\sum_{0\leq j\leq i}a_j\sum_{k=0}^{i-j}f_kf_{i-j-k}\\
\end{align}
\]
设
A(x)&=\sum_{i\geq 0}a_ix^i\\
F(x)&=\sum_{i\geq 0}f_ix^i
\end{align}
\]
那么
F(x)&=F(x)^2A(x)+1\\
A(x)F(x)^2-F(x)+1&=0\\
\Delta&=1-4A(x)\\
F(x)&=\frac{1\pm\sqrt{1-4A(x)}}{2A(x)}\\
&=\frac{2}{1\pm\sqrt{1-4A(x)}}
\end{align}
\]
显然,当常数项有逆元的时候一个多项式才有逆元。而\(a_0=0\),所以只能取加号。
\]
直接多项式开根+多项式求逆。
时间复杂度:\(O(n\log n)\)
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
if(a>b)
swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
int rd()
{
int s=0,c;
while((c=getchar())<'0'||c>'9');
do
{
s=s*10+c-'0';
}
while((c=getchar())>='0'&&c<='9');
return s;
}
int upmin(int &a,int b)
{
if(b<a)
{
a=b;
return 1;
}
return 0;
}
int upmax(int &a,int b)
{
if(b>a)
{
a=b;
return 1;
}
return 0;
}
const ll p=998244353;
const ll g=3;
ll fp(ll a,ll b)
{
ll s=1;
while(b)
{
if(b&1)
s=s*a%p;
a=a*a%p;
b>>=1;
}
return s;
}
const int maxn=600000;
ll inv[maxn];
namespace ntt
{
ll w1[maxn];
ll w2[maxn];
int rev[maxn];
int n;
void init(int m)
{
n=1;
while(n<m)
n<<=1;
int i;
for(i=2;i<=n;i<<=1)
{
w1[i]=fp(g,(p-1)/i);
w2[i]=fp(w1[i],p-2);
}
rev[0]=0;
for(i=1;i<n;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
}
void ntt(int *a,int t)
{
int i,j,k;
int u,v,w,wn;
for(i=0;i<n;i++)
if(rev[i]<i)
swap(a[i],a[rev[i]]);
for(i=2;i<=n;i<<=1)
{
wn=(t==1?w1[i]:w2[i]);
for(j=0;j<n;j+=i)
{
w=1;
for(k=j;k<j+i/2;k++)
{
u=a[k];
v=1ll*a[k+i/2]*w%p;
a[k]=(u+v)%p;
a[k+i/2]=(u-v)%p;
w=1ll*w*wn%p;
}
}
}
if(t==-1)
{
u=fp(n,p-2);
for(i=0;i<n;i++)
a[i]=1ll*a[i]*u%p;
}
}
int x[maxn];
int y[maxn];
int z[maxn];
void copy_clear(int *a,int *b,int m)
{
int i;
for(i=0;i<m;i++)
a[i]=b[i];
for(i=m;i<n;i++)
a[i]=0;
}
void copy(int *a,int *b,int m)
{
int i;
for(i=0;i<m;i++)
a[i]=b[i];
}
void inverse(int *a,int *b,int m)
{
if(m==1)
{
b[0]=fp(a[0],p-2);
return;
}
inverse(a,b,m>>1);
init(m<<1);
copy_clear(x,a,m);
copy_clear(y,b,m>>1);
ntt(x,1);
ntt(y,1);
int i;
for(i=0;i<n;i++)
x[i]=y[i]*(2-1ll*x[i]*y[i]%p)%p;
ntt(x,-1);
copy(b,x,m);
}
int c[maxn],d[maxn];
void sqrt(int *a,int *b,int m)
{
if(m==1)
{
if(a[0]==1)
b[0]=1;
else if(a[0]==0)
b[0]=0;
else
//我也不会
;
return;
}
sqrt(a,b,m>>1);
// copy_clear(c,b,m>>1);
int i;
for(i=m;i<m<<1;i++)
b[i]=0;
inverse(b,d,m);
init(m<<1);
for(i=m;i<m<<1;i++)
b[i]=d[i]=0;
ll inv2=fp(2,p-2);
copy_clear(x,a,m);
ntt(x,1);
ntt(d,1);
for(i=0;i<n;i++)
x[i]=1ll*x[i]*d[i]%p;
ntt(x,-1);
for(i=0;i<m;i++)
b[i]=(1ll*(b[i]+x[i])%p*inv2)%p;
}
};
int a[maxn];
int b[maxn];
int main()
{
open("bzoj3625");
int n,m;
int i;
int x;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
a[x]=1;
}
int k=1;
while(k<=m)
k<<=1;
for(i=0;i<k;i++)
a[i]=-4ll*a[i]%p;
a[0]=(a[0]+1)%p;
ntt::sqrt(a,b,k);
b[0]=(b[0]+1)%p;
ntt::inverse(b,a,k);
for(i=1;i<=m;i++)
{
a[i]=(a[i]*2ll%p+p)%p;
printf("%d\n",a[i]);
}
return 0;
}
【BZOJ3625】【CF438E】小朋友和二叉树 NTT 生成函数 多项式开根 多项式求逆的更多相关文章
- [BZOJ3625][CF438E]小朋友和二叉树 (多项式开根,求逆)
题面 题解 设多项式的第a项为权值和为a的二叉树个数,多项式的第a项表示是否为真,即 则,所以F是三个多项式的卷积,其中包括自己: ,1是F的常数项,即. 我们发现这是一个一元二次方程,可以求出,因为 ...
- [BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆)
[BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆) 题面 一棵二叉树的所有点的点权都是给定的集合中的一个数. 让你求出1到m中所有权 ...
- CF438E The Child and Binary Tree(生成函数+多项式开根+多项式求逆)
传送门 可以……这很多项式开根模板……而且也完全不知道大佬们怎么把这题的式子推出来的…… 首先,这题需要多项式开根和多项式求逆.多项式求逆看这里->这里,这里讲一讲多项式开根 多项式开方:已知多 ...
- BZOJ3625 CF438E 小朋友与二叉树
心态崩了 不放传送门了 辣鸡bz 还是正经一点写一下题解= = 就是显然我们可以把权值写成生成函数形式g(0/1序列)来表示权值是否出现 然后f来表示总的方案数 可以列出 分别枚举左右子树和空树的情况 ...
- [BZOJ3625][CF438E]小朋友和二叉树
题面 Description 我们的小朋友很喜欢计算机科学,而且尤其喜欢二叉树. 考虑一个含有\(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 ...
- cf438E. The Child and Binary Tree(生成函数 多项式开根 多项式求逆)
题意 链接 Sol 生成函数博大精深Orz 我们设\(f(i)\)表示权值为\(i\)的二叉树数量,转移的时候可以枚举一下根节点 \(f(n) = \sum_{w \in C_1 \dots C_n} ...
- 洛谷P5205 【模板】多项式开根(多项式sqrt)
题意 题目链接 Sol 这个就很没意思了 求个ln,然后系数除以2,然后exp回去. #include<bits/stdc++.h> #define Pair pair<int, i ...
- BZOJ 3625 [Codeforces Round #250]小朋友和二叉树 ——NTT 多项式求逆 多项式开根
生成函数又有奇妙的性质. $F(x)=C(x)*F(x)*F(x)+1$ 然后大力解方程,得到一个带根号的式子. 多项式开根有解只与常数项有关. 发现两个解只有一个是成立的. 然后多项式开根.求逆. ...
随机推荐
- 第十五次oo作业
作业十五 测试与正确性论证的效果差异 程序的测试需要通过输入特定数据等方式,检查程序是否和预期相同,因为测试不可能穷举,导致了不穷举的测试不可能验证程序是完全正确的,只能验证程序在测试时没有发生错误, ...
- Node.js api接口和SQL数据库关联
数据库表创建 服务器环境配置.连接 .操作.数据库 API接口 原则:
- 不能再忽视了!宝宝不肯吃粥的N个原因,你避免了几个?
辅食不懂怎么添加? 宝宝吃饭爱挑食? 营养均衡和多样化的辅食 在这里你都能找到 宝宝辅食微课堂 不能再忽视了!宝宝不肯吃粥的N个原因,你避免了几个? 2017-10-09 09:35 辅食不懂怎么添加 ...
- 多线程系列之十:Future模式
一,Future模式 假设有一个方法需要花费很长的时间才能获取运行结果.那么,与其一直等待结果,不如先拿一张 提货单.获取提货单并不耗费时间.这里提货单就称为Future角色获取Future角色的线程 ...
- Linux系统中常用的命令汇总
日常开发,上线的服务器系统一般都是Linux系统,所以,熟练的掌握常用的命令操作就尤其的重要了 1) 查看某个服务的运行情况 (例如Redis) ps -ef | grep redis //e-显示程 ...
- b,B,KB,MB,GB,TB,PB,EB,ZB,YB,BB,NB,DB的含义,之间的关系
1bit=1位2进制信息 1B (byte 字节)1KB(Kilobyte 千字节)=2(10)B=1024B=2(10)B: 1MB(Megabyte 兆字节)=2(10)KB=1024KB=2(2 ...
- vscode开发中绝对让你惊艳的插件!!!(个人在用)
识别模版引擎 1.Apache Velocity :识别Velocity(vm) 2.Art Template Helper:识别artTemplate 点击路径跳转 1.Laravel goto v ...
- java核心API学习
1:java.lang (Object.String.StringBuffer.Thread.System.ClassLoader.Class.Runtime.包装类等)
- centos 6.5 查看时区和设置时区
centos6.x 和centos7.x在时区方面有点差距,本文是针对centos6.x进行介绍. 其实在我的一个博文里,在安装系统的时候就可以进行时区的设置,本文介绍的是用命令进行时区查看和设置. ...
- K3BOM跳层
A自制件,B自制件,C外购件 ,结构为A-B-C 如果需要跳层,则设置A-B跳层,B-C跳层,则生成A计划订单,C计划订单, 假设单独A-B跳层,则MRP运算出的结果也是A计划订单,B计划订单,C计划 ...