zoukankan      html  css  js  c++  java
  • CF438E The Child and Binary Tree

    Description

    题目链接

    如果一个带权有根二叉树的所有节点的权值都在 ({c_1,c_2,c_3,...,c_n}) 中,那么我们就称它为好的二叉树,并定义其权值为所有点的权值之和。现在你需要对于所有的 (sin [1,m]) 计算出权值为 (s) 的不同的好的二叉树的数量,答案对 (998244353) 取模

    (1le nle 10^5,1le mle 10^5,1le c_ile 10^5)

    Solution

    显然若枚举所有的 (s) ,那么我们应该在 (log) 的时间内求出权值为 (s) 的二叉树的数量,这是不太现实的

    那么我们可以定义一个生成函数 (G(x)),用它的 (i) 次项系数表示权值为 (i) 的不同的好的二叉树的数量,又由于 (sin[1,m]),所以这种方法应该比较可行

    考虑怎么求 (G(x))

    在这之前我们先设 (g_i) 表示权值为 (i) 的不同的好的二叉树的数量并考虑如何转移

    由于是求二叉树有关的方案,所以递推关系与卡特兰数类似,即

    [g_i=sumlimits_{j=1}^{n}sumlimits_{k=0}^{i-c_j}g_kg_{i-c_j-k} ag{1} ]

    边界 (g_0=1)

    现在的问题在于这样没法写成生成函数的形式,因为 ((1)) 式相当于是把 ({g_n}) 的生成函数卷起来后每隔几位取一个然后再累加到新的一位上的

    每隔几位取一个?

    这提醒我们可以考虑构造一个函数,使得它乘上 ({g_n}) 的生成函数后恰好可以实现上述的效果

    其实已经不需要构造了,({c_n}) 的生成函数 (C(x)) 就可以满足我们的要求,即

    [[x^k]C(x)=[kin {c_n}] ag{2} ]

    至于为什么手动算算就知道了

    那么现在就很明朗了,将上述转移写成生成函数的形式

    [G(x)=C(x)G^2(x)+1 ag{3} ]

    因为 (C(x)) 是已知的,我们把它看作常数一样的东西,那么移项利用求根公式得到

    [G(x)=frac{1pm sqrt{1-4C(x)}}{2C(x)} ag{4} ]

    代入 (x=0) 判断解,由于 (C(0)=0​),那么我们得到最终的解为

    [G(x)=frac{1-sqrt{1-4C(x)}}{2C(x)} ag{5} ]

    但遗憾的是,因为多项式 (F(x)) 存在逆元的充要条件是 ([x^0]F(x) ot=0) ,而 (C(x)) 的常数项为 (0),所以它并不存在逆元

    回归到 ((3)) 式,既然 (C(x)) 不存在逆元,我们就不能让它在分母上,而 (C(x)) 是二次项的系数,所以考虑消去 (G^2(x))

    因为 (G(x)) 存在逆元,所以对两边同除以 (G(x)),得

    [frac{1}{G(x)}=C(x)+frac{1}{G^2(x)} ag{6} ]

    换元,设 (H(x)=G^{-1}(x)​),那么原式可化为

    [H^2(x)-H(x)+C(x)=0 ag{7} ]

    现在在利用求根公式,得到

    [H(x)=frac{1pm sqrt{1-4C(x)}}{2} ag{8} ]

    由于 (H(x)=G^{-1}(x)),那么必有 (H(0)=1),所以最终得到解为

    [H(x)=frac{1+sqrt{1-4C(x)}}{2} ag{9} ]

    那么最后的答案为

    [G(x)=frac{2}{1+sqrt{1-4C(x)}} ag{10} ]

    然后就做完了,复杂度 (O(nlog n))

    不得不说中间那步变换还是挺有意思的

    代码如下:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N=1e5+10;
    const int mod=998244353;
    const int G=3;
    const int invG=332748118;
    int n,m,A,k,c[N<<2],f[N<<2],p[N<<2],g[N<<2],d[N<<2],h[N<<2],q[N<<2],v[N<<2];
    inline void Add(int &x,int y){x+=y;x-=x>=mod? mod:0;}
    inline int MOD(int x){x-=x>=mod? mod:0;return x;}
    inline int Minus(int x){x+=x<0? mod:0;return x;}
    inline int fas(int x,int p){int res=1;while(p){if(p&1)res=1ll*res*x%mod;p>>=1;x=1ll*x*x%mod;}return res;}
    inline void NTT(int *a,int f){
        for(register int i=0,j=0;i<k;i++){
            if(i>j)swap(a[i],a[j]);
            for(register int l=k>>1;(j^=l)<l;l>>=1);}
        for(register int i=1;i<k;i<<=1){
            int w=fas(~f? G:invG,(mod-1)/(i<<1));
            for(register int j=0;j<k;j+=(i<<1)){
                int e=1;
                for(register int p=0;p<i;p++,e=1ll*e*w%mod){
                    int x=a[j+p],y=1ll*a[j+p+i]*e%mod;
                    a[j+p]=MOD(x+y);a[j+p+i]=MOD(x-y+mod);
                }
            }
        }
    }
    inline void PINV(int *a,int *b,int deg){
    	if(deg==1){b[0]=fas(a[0],mod-2);return;}
    	int M=(deg+1)>>1;PINV(a,b,M);
    	k=1;while(k<=deg+deg-2)k<<=1;
    	for(register int i=0;i<deg;i++)v[i]=a[i];
    	for(register int i=deg;i<k;i++)v[i]=0;
    	NTT(v,1);NTT(b,1);
    	for(register int i=0;i<k;i++)
    		b[i]=1ll*(2ll-1ll*v[i]*b[i]%mod+mod)*b[i]%mod;
    	NTT(b,-1);int INV=fas(k,mod-2);
    	for(register int i=0;i<deg;i++)b[i]=1ll*b[i]*INV%mod;
    	for(register int i=deg;i<k;i++)b[i]=0;
    }
    inline void Sqrt(int *a,int *b,int deg){
    	if(deg==1){b[0]=1;return;}
    	int M=(deg+1)>>1;Sqrt(a,b,M);
    	k=1;while(k<=deg+deg-2)k<<=1;int INV=fas(k,mod-2);
    	for(register int i=0;i<deg;i++)p[i]=b[i];
    	for(register int i=deg;i<k;i++)p[i]=0;
    	NTT(p,1);
    	for(register int i=0;i<k;i++)p[i]=1ll*p[i]*p[i]%mod;
    	NTT(p,-1);
    	for(register int i=0;i<deg;i++)p[i]=1ll*p[i]*INV%mod;
    	for(register int i=deg;i<k;i++)p[i]=0;
    	for(register int i=0;i<deg;i++)Add(p[i],a[i]);
    	for(register int i=0;i<deg;i++)d[i]=MOD(b[i]+b[i]);
    	for(register int i=deg;i<k;i++)d[i]=0;
    	for(register int i=0;i<k;i++)g[i]=0;
    	PINV(d,g,deg);
    	k=1;while(k<=deg+deg-2)k<<=1;
    	NTT(g,1);NTT(p,1);
    	for(register int i=0;i<k;i++)b[i]=1ll*g[i]*p[i]%mod;
    	NTT(b,-1);
    	for(register int i=0;i<deg;i++)b[i]=1ll*b[i]*INV%mod;
    	for(register int i=deg;i<k;i++)b[i]=0;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(register int i=1;i<=n;i++)scanf("%d",&A),c[A]++;
    	f[0]=1;for(register int i=1;i<=m;i++)f[i]=mod-4*c[i];
    	Sqrt(f,h,m+1);h[0]++;PINV(h,q,m+1);
    	for(register int i=0;i<=m;i++)q[i]=2ll*q[i]%mod;
    	for(register int i=1;i<=m;i++)printf("%d
    ",q[i]);
    	return 0;
    }
    
  • 相关阅读:
    机器重装后几个要记的问题
    JS中split用法和数组中元素的删除
    免费软件的盈利方式
    web程序的发布及相关问题
    无法直接启动带有“类库输出类型”的项目
    System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本
    orcale不同版本数据导入、导出及库版本查询
    VSc# web程序:gridview保存Excel文件遇到的问题
    油田生产中的几个“三”
    web跨页面传值——FORM表单(c#)
  • 原文地址:https://www.cnblogs.com/ForwardFuture/p/11538076.html
Copyright © 2011-2022 走看看