洛谷

Codeforces


思路

看到计数和\(998244353\),可以感觉到这是一个DP+生成函数+NTT的题。

设\(s_i\)表示\(i\)是否在集合中,\(A\)为\(s\)的生成函数,即\(A(x)=\sum_n s_nx^n\)。

设\(f_n\)为有\(n\)分时二叉树的个数。

考虑枚举左子树大小和根节点权值,得到

\[f_n=[n=0]+\sum_{i=1}^{mx} s_i \sum_{j=0}^{n-i} f_jf_{n-i-j}
\]

然后记\(F(x)\)为\(f\)的生成函数:

\[\begin{align*}
F(x)&=\sum_n f_nx^n\\
&=1+\sum_n x^n\sum_{i=1}^{mx} s_i \sum_{j=0}^{n-i} f_jf_{n-i-j}
\end{align*}
\]

可以发现\((i)+(j)+(n-i-j)=n\),即后面是三个多项式卷积在一起,得到\(F=1+A*F^2\)。

利用求根公式得到\(F=\frac{1\pm \sqrt{1-4A}}{2A}\)。

用某种奇怪的理论得到只能取减号,于是有

\[F=\frac{1-\sqrt{1-4A}}{2A}
\]

似乎做完了,但你会发现下面\(2A\)的常数项为\(0\),好像不能多项式求逆。(也许可以?大佬能教我一下吗?)

update:已经学会如何求逆,将其放在最后。

怎么办呢?我们可以把这个式子再化一下:

\[F=\frac{1-\sqrt{1-4A}}{2A}=\frac{2}{1+\sqrt{1-4A}}
\]

此时开根和求逆都没有问题,可以做了。


代码

#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define drep(i,x,y) for (int i=(x);i>=(y);i--)
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define sz 404040
#define mod 998244353
typedef long long ll;
template<typename T>
inline void read(T& t)
{
t=0;char f=0,ch=getchar();
double d=0.1;
while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
if(ch=='.')
{
ch=getchar();
while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();
}
t=(f?-t:t);
}
template<typename T,typename... Args>
inline void read(T& t,Args&... args){read(t); read(args...);}
void file()
{
#ifndef ONLINE_JUDGE
freopen("a.txt","r",stdin);
#endif
}
// inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std; ll ksm(ll x,int y)
{
ll ret=1;
for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;
return ret;
}
ll inv(ll x){return ksm(x,mod-2);} int limit,r[sz];
ll G[sz],_G[sz];
void init(){for (int mid=1;mid<sz;mid<<=1) _G[mid]=inv(G[mid]=ksm(3,(mod-1)/mid>>1));}
void NTT_init(int n)
{
limit=1;int l=-1;
while (limit<=n+n) limit<<=1,++l;
rep(i,0,limit-1) r[i]=(r[i>>1]>>1)|((i&1)<<l);
}
void NTT(ll *a,int type)
{
rep(i,0,limit-1) if (i<r[i]) swap(a[i],a[r[i]]);
rep(i,0,limit-1) a[i]%=mod;
for (int mid=1;mid<limit;mid<<=1)
{
ll Wn=(type==1)?G[mid]:_G[mid];
for (int j=0,len=mid<<1;j<limit;j+=len)
{
ll w=1;
for (int k=0;k<mid;k++,w=w*Wn%mod)
{
ll x=a[j+k],y=a[j+k+mid]*w;
a[j+k]=(x+y)%mod;a[j+k+mid]=(1ll*mod*mod-y+x)%mod;
}
}
}
if (type==1) return;
ll I=inv(limit);
rep(i,0,limit-1) a[i]=a[i]*I%mod;
}
ll tmp1[sz],tmp2[sz],tmp3[sz],tmp4[sz];
void PolyInv(ll *a,ll *f,int n) // f=a^{-1}
{
if (n==1) return (void)(f[0]=inv(a[0]));
int mid=(n+1)>>1;
PolyInv(a,f,mid);
NTT_init(n);
rep(i,0,mid-1) tmp1[i]=f[i];rep(i,mid,limit-1) tmp1[i]=0;
rep(i,0,n-1) tmp2[i]=a[i];rep(i,n,limit-1) tmp2[i]=0;
NTT(tmp1,1);NTT(tmp2,1);
rep(i,0,limit-1) tmp1[i]=tmp1[i]*(mod+2-tmp1[i]*tmp2[i]%mod)%mod;
NTT(tmp1,-1);
rep(i,0,n-1) f[i]=tmp1[i];
rep(i,0,limit-1) tmp1[i]=tmp2[i]=0;
}
void PolySqrt(ll *a,ll *f,int n)
{
if (n==1) return (void)(f[0]=sqrt(a[0])+0.5);
int mid=(n+1)>>1;
PolySqrt(a,f,mid);
PolyInv(f,tmp3,n);
rep(i,0,n-1) tmp1[i]=a[i],tmp2[i]=f[i];
NTT_init(n);
NTT(tmp1,1);NTT(tmp2,1);NTT(tmp3,1);
ll inv2=inv(2);
rep(i,0,limit-1) tmp1[i]=inv2*(tmp1[i]*tmp3[i]%mod+tmp2[i])%mod;
NTT(tmp1,-1);
rep(i,0,n-1) f[i]=tmp1[i];
rep(i,0,limit-1) tmp1[i]=tmp2[i]=tmp3[i]=0;
} int n,m;
ll a[sz];
ll tt1[sz],tt2[sz];
ll ans[sz]; int main()
{
file();
init();
read(n,m);
int x;
rep(i,1,n) read(x),a[x]=1;
tt1[0]=1;rep(i,1,m) tt1[i]=mod-4*a[i]%mod;
PolySqrt(tt1,tt2,m+1);
++tt2[0];
PolyInv(tt2,ans,m+1);
rep(i,1,m) ans[i]=ans[i]*2%mod;
rep(i,1,m) printf("%lld\n",ans[i]);
return 0;
}

更新

前文说到不知道怎么多项式求逆,现在学会了更新一发。

考虑到你推出的式子肯定是正确的(不正确我也没办法),那么下面多出来的那些\(x^n\)肯定可以被上面消掉,否则就没法做了。

那么可以先把\(\min\{a_i\}\)(记作\(mn\))提出来,然后给下面求逆。然后上面开完根之后整体除一个\(x^{mn}\)即可。

然而这样会有一些奇怪的事情发生:原来全都对\(x^{m+1}\)取模,现在要搞一些事情,对\(x^{\max(m,\max\{a_i\})+mn+1}\)取模。(或者你懒的话直接对\(x^{200000}\)取模也可以)

这样常数会大一些,但也可以通过。

代码:(只有主函数)

int n,m;
ll a[sz];int mn=1e9,mx;
ll tt1[sz],sqr[sz],tt2[sz],inva[sz];
ll ans[sz]; int main()
{
file();
init();
read(n,m);
int x;
rep(i,1,n) read(x),a[x]=1,mn=min(mn,x),mx=max(mx,x);
mx=max(mx,m);
tt1[0]=1;rep(i,1,mx) tt1[i]=(mod-4*a[i])%mod;
rep(i,0,mx) tt2[i]=2*a[i+mn];
PolySqrt(tt1,sqr,mx+1+mn);
PolyInv(tt2,inva,mx+1);
rep(i,0,mx) sqr[i]=(mod-sqr[i+mn])%mod;
NTT_init(mx+1);
rep(i,mx+1,limit-1) sqr[i]=inva[i]=0;
NTT(inva,1);NTT(sqr,1);
rep(i,0,limit-1) ans[i]=inva[i]*sqr[i]%mod;
NTT(ans,-1);
rep(i,1,m) printf("%lld\n",ans[i]);
return 0;
}

Codeforces 438E The Child and Binary Tree [DP,生成函数,NTT]的更多相关文章

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

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

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

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

  3. [题解] Codeforces 438 E The Child and Binary Tree DP,多项式,生成函数

    题目 首先令\(f_i\)表示权值和为\(i\)的二叉树数量,\(f_0=1\). 转移为:\(f_k=\sum_{i=0}^n \sum_{j=0}^{k-c_i}f_j f_{k-c_i-j}\) ...

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

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

  5. [bzoj3625][Codeforces 250 E]The Child and Binary Tree(生成函数+多项式运算+FFT)

    3625: [Codeforces Round #250]小朋友和二叉树 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 650  Solved: 28 ...

  6. bzoj 3625(CF 438E)The Child and Binary Tree——多项式开方

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3625 http://codeforces.com/contest/438/problem/E ...

  7. CF 438E The Child and Binary Tree

    BZOJ 3625 吐槽 BZOJ上至今没有卡过去,太慢了卡得我不敢交了…… 一件很奇怪的事情就是不管是本地还是自己上传数据到OJ测试都远远没有到达时限. 本题做法 设$f_i$表示权值为$i$的二叉 ...

  8. CF438E The Child and Binary Tree(生成函数,NTT)

    题目链接:洛谷 CF原网 题目大意:有 $n$ 个互不相同的正整数 $c_i$.问对于每一个 $1\le i\le m$,有多少个不同形态(考虑结构和点权)的二叉树满足每个点权都在 $c$ 中出现过, ...

  9. CF438E The Child and Binary Tree(生成函数+多项式开根+多项式求逆)

    传送门 可以……这很多项式开根模板……而且也完全不知道大佬们怎么把这题的式子推出来的…… 首先,这题需要多项式开根和多项式求逆.多项式求逆看这里->这里,这里讲一讲多项式开根 多项式开方:已知多 ...

随机推荐

  1. json日期字符串格式化时间

    var str = '/Date(1333245600000+0800)/';   function data_string(str) {     var d = eval('new ' + str. ...

  2. I/O 流

    输入流的几个常用方法: 1,复制一个文件; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; impor ...

  3. UE4 编译笔记

    UE4 的功能被分成了很多的模块,在API文档里每个类都有 Module 这个属性(值为模块名)每个模块使用其他模块要在Build.cs里导入,像vs里的引入库.在构建时 PublicDependen ...

  4. linux下搭建lamp环境以及安装swoole扩展

    linux下搭建lamp环境以及安装swoole扩展   一.CentOS 6.5使用yum快速搭建LAMP环境 准备工作:先更新一下yum源  我安装的环境是:apache2.2.15+mysql5 ...

  5. LOJ #2542「PKUWC2018」随机游走

    $ Min$-$Max$容斥真好用 $ PKUWC$滚粗后这题一直在$ todolist$里 今天才补掉..还要更加努力啊.. LOJ #2542 题意:给一棵不超过$ 18$个节点的树,$ 5000 ...

  6. 【tmos】SpringBoot项目IDEA不识别依赖,但是能够运行时什么鬼?

    如下图,但是项目却能够正常运行 解决方法 方法1 方法2 方法3 删除项目的.idea文件,然后重新打开IDEA 方法4 删除项目的iml文件,然后reimport项目的pom.xml 文件,错误提示 ...

  7. Invalid bound statement (not found) 找不到mapper 映射文件异常

    访问页面报如下错(注意第一行后面的 invalid bound statement (not found)) 这时候再mapper的pom.xml文件要加如下. 否则该节点mybatis的mapper ...

  8. mongodb系列~mongodb定时删除数据

    一 简介:本文介绍创建自动删除数据的TTL索引 二 目的 定时删除数据三 创建方法   db.collection.createIndex(keys, options)   options:   ex ...

  9. Django学习手册 - pycharm 安装/建立第一个网站hello world

    步骤阐述: 1.下载 pycharm 安装包,安装pycharm 2.打开pycharm软件,新建工程项目 3.新建APP,配置url,启动项目 步骤1: pycharm 官网下载: https:// ...

  10. 2017-2018-2 20165234 实验二 《Java面向对象程序设计》实验报告

    一.实验报告封面 课程:Java程序设计  班级:1652班  姓名:刘津甫  学号:20165234 指导教师:娄嘉鹏  实验日期:2018年4月13日 实验时间:15:35 - 17:15  实验 ...