[Codeforces438E]The Child and Binary Tree(多项式开根+多项式求逆)

=== ===

这里放传送门

=== ===

题解

这道题的意思是给定一个集合,求有多少形态不同的二叉树满足每个点的权值都属于这个集合并且总点权等于i。
F(x) 表示总点权等于x的的二叉树数目,那么由当前已经有的二叉树构造更大的二叉树就是先把两个子树拼起来然后上面再挂一个新点作为根。显然这都可以表示成生成函数运算。那么就是 F(x)×F(x)×C(x) C(x) 表示读入的集合的生成函数。
然而这样算出来的这一坨东西就等于 F(x) 了吗?并不是,因为在计算的时候为了让它能正确地累加只有一个根节点的那种树, F(x) 必须有一个常数项1。但是做完了上面那种运算以后那个常数项就没有了,所以要给它加回去。也就是 F(x)=F2(x)×C(x)+1
这是一个生成函数的一元二次方程,把它解出来得到 F(x)=1±14×C(x)2×C(x)
然而这好像并不是一个有多解的问题,于是观察解出来的这两个多项式。
诶这样一看好像两个并没有什么区别啊都挺可以的啊???
那分子分母上都有C(x)看起来不是很友好,我们把上下同时乘以 114×C(x) ,可以发现式子变成了 21±14×C(x)
这样子看起来就比较好办了,因为当x趋近于0的时候C(x)也趋近于0,如果下面取的是 114×C(x) 的话分母就会变成0,这是不可以的,那么解就是 F(x)=21+14×C(x)
然后多项式开根和多项式求逆。。。。
多项式怎么开根????
ATP懒得写了丢链接跑[戳这里]

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Mod=998244353;
const int G=3;
const int inv2=499122177;
int n,m,R[300010],N,M,inv[300010],rt[300010],v[300010],c[300010],tmp[300010];
int powww(int a,int t){
    int ans=1;a%=Mod;
    while (t!=0){
        if (t&1) ans=((long long)ans*(long long)a)%Mod;
        a=((long long)a*(long long)a)%Mod;t>>=1;
    }
    return ans;
}
void NTT(int N,int *a,int opt){
    int w,wn,x,y;
    for (int i=1;i<=N;i++)
      if (i<R[i]) swap(a[i],a[R[i]]);
    for (int k=1;k<N;k<<=1){
        wn=powww(G,(Mod-1)/(k<<1));
        for (int i=0,p=(k<<1);i<N;i+=p){
            w=1;
            for (int j=0;j<k;j++){
                x=a[i+j];y=(long long)w*(long long)a[i+j+k]%Mod;
                a[i+j]=(x+y)%Mod;
                a[i+j+k]=(x-y)%Mod;
                w=(long long)w*(long long)wn%Mod;
            }
        }
    }
    if (opt==-1) reverse(a+1,a+N);
}
void Inverse(int N,int *a,int *b){
    if (N==1){b[0]=powww(a[0],Mod-2);return;}
    int K=(N<<1),L=0;
    long long inv=powww(K,Mod-2);
    Inverse(N>>1,a,b);
    for (int i=0;i<N;i++) tmp[i]=a[i];
    for (int i=N;i<=K;i++) tmp[i]=0;
    for (int i=1;i<K;i<<=1) L++;
    for (int i=0;i<=K;i++)
      R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    NTT(K,tmp,1);NTT(K,b,1);
    for (int i=0;i<K;i++)
      tmp[i]=(long long)b[i]*(long long)(2-(long long)b[i]*(long long)tmp[i]%Mod)%Mod;
    NTT(K,tmp,-1);
    for (int i=0;i<N;i++) b[i]=(long long)tmp[i]*(long long)inv%Mod;
    for (int i=N;i<=K;i++) b[i]=0;
}
void SqRoot(int N,int *a,int *b){
    if (N==1){b[0]=1;return;}
    int L=0,K=N<<1;
    int inv=powww(K,Mod-2);
    SqRoot(N>>1,a,b);
    for (int i=0;i<=N;i++) v[i]=0;
    Inverse(N,b,v);
    for (int i=0;i<N;i++) tmp[i]=a[i];
    for (int i=N;i<=K;i++) tmp[i]=0;
    for (int i=1;i<K;i<<=1) L++;
    for (int i=0;i<=K;i++)
      R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    NTT(K,tmp,1);NTT(K,v,1);NTT(K,b,1);
    for (int i=0;i<K;i++)
      tmp[i]=(long long)inv2*(long long)(b[i]+(long long)v[i]*(long long)tmp[i]%Mod)%Mod;
    NTT(K,tmp,-1);
    for (int i=0;i<N;i++) b[i]=(long long)tmp[i]*(long long)inv%Mod;
    for (int i=N;i<=K;i++) b[i]=0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        c[x]=-4;
    }
    c[0]=1;
    for (N=1;N<=m;N<<=1);
    SqRoot(N,c,rt);
    rt[0]++;Inverse(N,rt,inv);
    for (int i=1;i<=m;i++)
      inv[i]=(inv[i]*2%Mod+Mod)%Mod;
    for (int i=1;i<=m;i++) printf("%d\n",inv[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值