[题解] 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}\)

令多项式\(D=\sum_{i=0}^m [i在c中出现过]x^i\)\(F(x)为f的普通生成函数\),根据转移式发现F其实等于F卷积上F再卷积上D,再加上一个1,因为转移式转移不到\(f_0\)

所以

\[\begin{align} F&=F^2D+1\\ DF^2-F+1&=0\\ D^2F^2-DF+D&=0\\ (DF-\frac12)^2+D-\frac14&=0(配方)\\ (DF-\frac12)^2&=\frac14-D\\ DF-\frac12&=\pm \sqrt{\frac14-D}\\ \end{align} \]

有两解,但是\(f_i\)肯定只有一种可能啊所以肯定有一个解不合法。


当取正号时:

\(DF-\frac12=\sqrt{\frac14-D}\)\(DF=\frac{\sqrt{1-4D}+1}{2}\)

等式右边的常数项为1,但是\(D\)的常数项为0,\(F\)的常数项为1,所以\(DF\)的常数项为0,矛盾。

所以只能取负号。

\[\begin{align} DF&=\frac{1-\sqrt{1-4D}}{2}\\ F&=\frac{(1-\sqrt{1-4D})(1+\sqrt{1-4D})}{2D(1+\sqrt{1-4D})}\\ F&=\frac{2}{1+\sqrt{1-4D}}\\ \end{align}\\ \]

接下来直接多项式开根+求逆就行了。时间复杂度\(O(n logn)\)

小知识:多项式能求逆的充要条件是常数项不为0,常数项>0则一定能开根。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <LL,LL>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

using namespace std;

const LL MOD=998244353;

LL qpow(LL x,LL a)
{
	LL res=x,ret=1;
	while(a>0)
	{
		if((a&1)==1) ret=ret*res%MOD;
		a>>=1;
		res=res*res%MOD;
	}
	return ret;
}

namespace poly
{
  vector <LL> rev;
  void ntt(vector <LL> &a,LL G)
  {
    LL nn=a.size(),gn,g,x,y;vector <LL> tmp=a;
    rep(i,nn) a[i]=tmp[rev[i]];
    for(int len=1;len<nn;len<<=1)
    {
      gn=qpow(G,(MOD-1)/(len<<1));
      for(int i=0;i<nn;i+=(len<<1))
      {
        g=1;
        for(int j=i;j<i+len;++j,(g*=gn)%=MOD)
        {
          x=a[j];y=a[j+len]*g%MOD;
          a[j]=(x+y)%MOD;a[j+len]=(x-y+MOD)%MOD;
        }
      }
    }
  }
  vector <LL> convolution(vector <LL> a,vector <LL> b,LL G)
  {
    LL nn=1,bt=0,sv=a.size()+b.size()-1;while(nn<a.size()+b.size()-1) nn<<=1LL,++bt;
    while(a.size()<nn) a.pb(0);while(b.size()<nn) b.pb(0);
    rev.clear();
    rep(i,nn)
    {
      rev.pb(0);
      rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1));
    }
    ntt(a,G);ntt(b,G);
    rep(i,nn) (a[i]*=b[i])%=MOD;
    ntt(a,qpow(G,MOD-2));
    while(a.size()>sv) a.pop_back();
    LL inv=qpow(nn,MOD-2);
    rep(i,a.size()) (a[i]*=inv)%=MOD;
    return a;
  }
  vector <LL> inverse(vector <LL> a,LL G)
  {
    if(a.size()==1) return vector <LL>{qpow(a[0],MOD-2)};
    vector <LL> aa=a;while(aa.size()>(a.size()+1)>>1) aa.pop_back();
    vector <LL> bb=inverse(aa,G);
    LL nn=1,bt=0,sv=a.size();while(nn<a.size()*2) nn<<=1LL,++bt;
    while(a.size()<nn) a.pb(0);while(bb.size()<nn) bb.pb(0);
    rev.clear();
    rep(i,nn)
    {
      rev.pb(0);
      rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1));
    }
    ntt(a,G);ntt(bb,G);
    rep(i,nn) a[i]=(2LL-a[i]*bb[i]%MOD+MOD)*bb[i]%MOD;
    ntt(a,qpow(G,MOD-2));
    while(a.size()>sv) a.pop_back();
    LL inv=qpow(nn,MOD-2);
    rep(i,a.size()) (a[i]*=inv)%=MOD;
    return a;
  }
  vector <LL> sqrt1(vector <LL> a,LL G)//常数项为1
  {
    if(a.size()==1) return vector <LL>{1};
    vector <LL> aa=a;while(aa.size()>(a.size()+1)>>1) aa.pop_back();
    vector <LL> bb=sqrt1(aa,G);while(bb.size()<a.size()) bb.pb(0);
    vector <LL> bbb=inverse(bb,G);
    LL nn=1,bt=0,sv=a.size();while(nn<a.size()*2) nn<<=1LL,++bt;
    while(a.size()<nn) a.pb(0);while(bb.size()<nn) bb.pb(0);while(bbb.size()<nn) bbb.pb(0);
    rev.clear();
    rep(i,nn)
    {
      rev.pb(0);
      rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1));
    }
    LL mul=qpow(2,MOD-2);
    ntt(a,G);ntt(bb,G);ntt(bbb,G);
    rep(i,nn) a[i]=mul*(bb[i]+bbb[i]*a[i]%MOD)%MOD;
    ntt(a,qpow(G,MOD-2));
    while(a.size()>sv) a.pop_back();
    LL inv=qpow(nn,MOD-2);
    rep(i,a.size()) (a[i]*=inv)%=MOD;
    return a;
  }
}

LL n,m;
vector <LL> c;

int main()
{
  cin>>n>>m;
  rep(i,100001) c.pb(0);
  LL x;
  rep(i,n)
  {
    scanf("%lld",&x);
    c[x]=1;
  }
  c[0]=1;
  repn(i,100000) c[i]=MOD-c[i]*4LL;
  c=poly::sqrt1(c,3);
  (c[0]+=1LL)%=MOD;
  c=poly::inverse(c,3);
  rep(i,c.size()) (c[i]+=c[i])%=MOD;
  repn(i,m) printf("%lld\n",c[i]);
	return 0;
}
posted @ 2022-05-07 14:54  LegendStane  阅读(63)  评论(0)    收藏  举报