题目链接:https://www.luogu.com.cn/problem/CF438E
每个节点有n个权值可以选择,对于1∼m中的每个数字k,求权值和为k的二叉树个数。
设fn表示权值和为n的方案数,gn表示n这个权值是否可用。
那么我们对于一个n的转移,可以枚举根节点的权值,然后再用f去计算子节点的权值,具体的式子是
fn=n∑w=1gwn−w∑i=0fifn−w−i
会发现这个三个数的下标和就是n,这其实一个大卷积,设多项式F[x]=fx和G[x]=gx。那么根据上面式子就有
F=F2G+1
(加1是因为f0=1,然后可以解出式子)
F=1±√1−4G2G
这里的±我们取负号,因为取正号时不满足收敛性。
当然我也不知道怎么判正负,但是这题发现F[0]=1但是G[0]=1。又有2GF=1±√1−4G,显然有(2GF)[0]=0。但是如果取正号,那么(1+√1−4G)[0]≥1,所以显然不可能,只能取负。
然后可以直接上多项式开根和求逆做?发现G[0]=0不能求逆,只好再化一下式子变成
F=21+√1−4G
时间复杂度O(nlogn)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=8e5+10,P=998244353,inv2=(P+1)/2;
ll n,m,a[N],b[N];
ll power(ll x,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*x%P;
x=x*x%P;b>>=1;
}
return ans;
}
namespace Poly{
ll n,t1[N],t2[N],t3[N],t4[N],r[N];
void GetL(ll l){
n=1;while(n<=l)n<<=1;
for(ll i=0;i<n;i++)
r[i]=(r[i>>1]>>1)|((i&1)?(n>>1):0);
return;
}
void NTT(ll *f,ll op){
for(ll i=0;i<n;i++)
if(i<r[i])swap(f[i],f[r[i]]);
for(ll p=2;p<=n;p<<=1){
ll len=p>>1,tmp=power(3,(P-1)/p);
if(op==-1)tmp=power(tmp,P-2);
for(ll k=0;k<n;k+=p){
ll buf=1;
for(ll i=k;i<k+len;i++){
ll tt=f[i+len]*buf%P;
f[i+len]=(f[i]-tt+P)%P;
f[i]=(f[i]+tt)%P;
buf=buf*tmp%P;
}
}
}
if(op==-1){
ll invn=power(n,P-2);
for(ll i=0;i<n;i++)
f[i]=f[i]*invn%P;
}
return;
}
void GetInv(ll *f,ll *g,ll m){
if(m==1){g[0]=power(f[0],P-2);return;}
GetInv(f,g,m>>1);GetL(m);
for(ll i=0;i<m;i++)t1[i]=f[i],t2[i]=g[i];
for(ll i=m;i<n;i++)t1[i]=t2[i]=0;
NTT(t1,1);NTT(t2,1);
for(ll i=0;i<n;i++)t1[i]=t1[i]*t2[i]%P*t2[i]%P;
NTT(t1,-1);
for(ll i=0;i<m;i++)g[i]=(2*g[i]-t1[i]+P)%P;
return;
}
void GetSqrt(ll *f,ll *g,ll m){
if(m==1){g[0]=1;return;}
GetSqrt(f,g,m>>1);
for(ll i=0;i<m;i++)t3[i]=0;
GetInv(g,t3,m);GetL(m<<1);
for(ll i=0;i<m;i++)t4[i]=f[i];
for(ll i=m;i<n;i++)t3[i]=t4[i]=0;
NTT(t3,1);NTT(t4,1);
for(ll i=0;i<n;i++)t3[i]=t3[i]*t4[i]%P;
NTT(t3,-1);
for(ll i=0;i<m;i++)
g[i]=(g[i]+t3[i])*inv2%P;
return;
}
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(ll i=0;i<n;i++){
ll x;
scanf("%lld",&x);
if(x>m)continue;
a[x]=P-4;
}
ll l=1;a[0]++;
while(l<=m)l<<=1;
Poly::GetSqrt(a,b,l);
memset(a,0,sizeof(a));
b[0]++;Poly::GetInv(b,a,l);
for(ll i=1;i<=m;i++)
printf("%lld\n",a[i]*2%P);
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构