CF438E-The Child and Binary Tree【生成函数】
正題
題目鏈接:https://www.luogu.com.cn/problem/CF438E
題目大意
每個節點有nnn個權值可以選擇,對于1~m1\sim m1~m中的每個數字kkk,求權值和為kkk的二叉樹個數。
解題思路
設fnf_nfn?表示權值和為nnn的方案數,gng_ngn?表示nnn這個權值是否可用。
那么我們對于一個nnn的轉移,可以枚舉根節點的權值,然后再用fff去計算子節點的權值,具體的式子是
fn=∑w=1ngw∑i=0n?wfifn?w?if_n=\sum_{w=1}^ng_w\sum_{i=0}^{n-w}f_if_{n-w-i}fn?=w=1∑n?gw?i=0∑n?w?fi?fn?w?i?
會發現這個三個數的下標和就是nnn,這其實一個大卷積,設多項式F[x]=fxF[x]=f_xF[x]=fx?和G[x]=gxG[x]=g_xG[x]=gx?。那么根據上面式子就有
F=F2G+1F=F^2G+1F=F2G+1
(加1是因為f0=1f_0=1f0?=1,然后可以解出式子)
F=1±1?4G2GF=\frac{1\pm \sqrt{1-4G}}{2G}F=2G1±1?4G??
這里的±\pm±我們取負號,因為取正號時不滿足收斂性。
當然我也不知道怎么判正負,但是這題發現F[0]=1F[0]=1F[0]=1但是G[0]=1G[0]=1G[0]=1。又有2GF=1±1?4G2GF=1\pm\sqrt{1-4G}2GF=1±1?4G?,顯然有(2GF)[0]=0(2GF)[0]=0(2GF)[0]=0。但是如果取正號,那么(1+1?4G)[0]≥1(1+\sqrt{1-4G})[0]\geq1(1+1?4G?)[0]≥1,所以顯然不可能,只能取負。
然后可以直接上多項式開根和求逆做?發現G[0]=0G[0]=0G[0]=0不能求逆,只好再化一下式子變成
F=21+1?4GF=\frac{2}{1+\sqrt{1-4G}}F=1+1?4G?2?
時間復雜度O(nlog?n)O(n\log n)O(nlogn)
code
#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; }總結
以上是生活随笔為你收集整理的CF438E-The Child and Binary Tree【生成函数】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 斗鱼直播的电脑配置要求(斗鱼直播的电脑配
- 下一篇: P3235-[HNOI2014]江南乐【