洛谷

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. 大规模数据导入和导出(sqlserver)

    请期待... https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools#RHEL msodbcsql-13.1.6 ...

  2. 新闻类App顶部菜单栏封装

    概述 最近有一个需求,类似今日头条顶部的菜单栏.唯一区别是需要带可移动的下划线.网上查找资料,发现解决方案大部分是用UIScrollView实现.下方VC控制用UICollectionView.这样可 ...

  3. Oracle 数据库监听配置和服务

    -- 补充说明 如果要远程连接192.168.10.44上的oracle,那么192.168.10.44服务器必须启动TNSListener.(配置文件 listener.ora) PLSQL Dev ...

  4. web渗透测试基本步骤

       基本常见步骤: 一 .信息收集 要检测一个站首先应先收集信息如whois信息.网站真实IP.旁注.C段网站.服务器系统版本.容器版本.程序版本.数据库类型.二级域名.防火墙.维护者信息有哪些等等 ...

  5. 第25月第9天 tf_tang_poems kaggle

    1.neural-style https://github.com/anishathalye/neural-style wget http://www.vlfeat.org/matconvnet/mo ...

  6. 快速安装nginx

    1.创建nignx用户 /usr/sbin/groupadd -f nginx /usr/sbin/useradd -g nginx nginx 2.安装依赖 yum install gcc gcc- ...

  7. 浏览器支持webp格式

    使用插件http://www.etherdream.com/WebP/WebP.js

  8. (6)Java数据结构-- 转:JAVA常用数据结构及原理分析

    JAVA常用数据结构及原理分析  http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balab ...

  9. Java开发环境配置(4)--Maven安装 环境变量配置,本地仓库配置---插件安装

    说明在前:本人用过的 luna  mars 等,都已经自带maven插件的了,以下有些文章是很老的,讲到maven插件的安装都可以忽略掉. maven安装eclipse在线配置maven搞定所有插件_ ...

  10. Gulp简明使用教程

    Glup用自动化构建工具增强你的工作流程! 同类型的软件还有Grunt.关于两者的区别可以参考这篇文章Grunt VS Gulp 安装: $ npm install gulp -g $ npm ins ...