zoukankan      html  css  js  c++  java
  • 【CF438E】The Child and Binary Tree(生成函数+多项式开根)

    点此看题面

    大致题意: 给你(n)个数,对于(i∈[1,m]),求出有多少棵不同的二叉树,满足权值都取自这(n)个数中,且权值和恰为(i)

    推式子

    我们令(f_i)表示权值和恰为(i)的二叉树个数,(g_i)表示(i)是否为给出的数((0/1))。

    对于(f_0),易知(f_0=1)

    对于(f_n(n>0)),一个显然的转移,我们枚举(i)表示根节点填的数,然后枚举(j)表示左子树权值和,就有:

    [f_n=sum_{i=0}^ng_isum_{j=0}^{n-i}f_{j}f_{V-i-j} ]

    然后发现这其实是一个卷积的形式。

    (F,G)分别为(f,g)生成函数,得到:(注意最后的加(1)是因为(f_0=1)

    [F=GF^2+1 ]

    接下来,最神奇的一步来了。根据一元二次方程的求根公式,得到:

    [F=frac{1±sqrt{1-4G}}{2G} ]

    考虑当(F=frac{1+sqrt{1-4G}}{2G})时,(F)是不收敛的,故舍去。

    而当(F=frac{1-sqrt{1-4G}}{2G})时,(F)就能够满足要求了。

    此时还可以进一步转化让式子更简单:分母无理化分子有理化。得到:

    [F=frac2{1+sqrt{1-4G}} ]

    这样一来只要多项式开根+多项式求逆即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define V 100000
    #define X 998244353
    using namespace std;
    int n,m,g[V<<2],s[V<<2];
    I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    class Poly//多项式
    {
    	private:
    		int P,L,R[V<<2],t[V<<2],k[V<<2],PR,IPR,I2;
    		I void T(int *s,CI p)//NTT
    		{
    			RI i,j,k,U,S,x,y;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x);
    			for(i=1;i^P;i<<=1) for(U=QP(p,(X-1)/(i<<1)),j=0;j^P;j+=i<<1) for(S=1,k=0;k^i;
    				S=1LL*S*U%X,++k) s[j+k]=((x=s[j+k])+(y=1LL*S*s[i+j+k]%X))%X,s[i+j+k]=(x-y+X)%X;
    		}
    	public:
    		I Poly() {PR=3,IPR=QP(3,X-2),I2=X+1>>1;}
    		I void Inv(CI n,int *a,int *b)//多项式求逆
    		{
    			if(!n) return (void)(b[0]=QP(a[0],X-2));Inv(n>>1,a,b);
    			RI i;P=1,L=0;W(P<=(n<<1)) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
    			for(i=0;i<=n;++i) t[i]=a[i];W(i^P) t[i++]=0;
    			for(T(b,PR),T(t,PR),i=0;i^P;++i) b[i]=(2LL*b[i]-1LL*t[i]*b[i]%X*b[i]%X+X)%X;
    			RI t=QP(P,X-2);for(T(b,IPR),i=0;i<=n;++i) b[i]=1LL*b[i]*t%X;W(i^P) b[i++]=0;
    		}
    		I void Sqrt(CI n,int *a,int *b)//多项式开根
    		{
    			if(!n) return (void)(b[0]=1);Sqrt(n>>1,a,b);
    			RI i;P=1,L=0;W(P<=(n<<1)) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
    			for(i=0;i^P;++i) k[i]=0;for(Inv(n,b,k),i=0;i<=n;++i) t[i]=a[i];W(i^P) t[i++]=0;
    			for(T(t,PR),T(b,PR),T(k,PR),i=0;i^P;++i) b[i]=(1LL*b[i]*b[i]+t[i])%X*k[i]%X*I2%X;
    			RI t=QP(P,X-2);for(T(b,IPR),i=0;i<=n;++i) b[i]=1LL*b[i]*t%X;W(i^P) b[i++]=0;
    		}
    }P;
    int main()
    {
    	RI i,x;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&x),g[x]=1;
    	for(g[0]=i=1;i<=V;++i) g[i]=X-4LL*g[i]%X;P.Sqrt(V,g,s),++s[0]==X&&(s[0]=0);//把g修改为1-4g,令s=1+sqrt(1-4g)
    	for(memset(g,0,sizeof(g)),P.Inv(V,s,g),i=1;i<=m;++i) printf("%d
    ",2LL*g[i]%X);//把g修改为1/(1+sqrt(1-4g)),系数乘2即为答案
    	return 0;
    }
    
  • 相关阅读:
    MPX
    Centos7 LVM扩充存储空间
    capjoint conversations with Chenweiwen
    STA/LTA方法
    capjoint一些生成文件的解释
    搜索震源机制解
    git学习教程
    SAC处理命令transfer的一些详细介绍
    fastjson学习笔记
    语法检查程序LanguageTool学习和使用笔记
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF438E.html
Copyright © 2011-2022 走看看