【BZOJ3625/CF438E】小朋友和二叉树(多项式求逆,多项式开方)

15 篇文章 0 订阅
3 篇文章 0 订阅

题面

BZOJ
CodeForces
大致题意:
对于每个数出现的次数对应的多项式 A(x)

f(x)=24A(x)+1+1

题解

多项式开方+多项式求逆模板题
我之前写的多项式求逆很丑,常数大的惊人
成功拿到洛谷模板题倒数第一的速度

于是,我学习了一波Gay神的写法
写了一下这道题目

具体的细节暂时不写了,以后肯定有机会的写的(这点我可以保证)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 500000
#define MOD 998244353
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,m,inv2,d[MAX];
int fpow(int a,int b)
{
    int s=1;
    while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
    return s;
}
namespace NTT
{
    int r[MAX],N,M,l;
    int A[MAX],B[MAX];
    void NTT(int *P,int n,int opt)
    {
        int N=1,l=0;for(N=1;N<n;N<<=1)++l;
        for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
        for(int i=1;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
        for(int i=1;i<N;i<<=1)
        {
            int W=fpow(3,(MOD-1)/(i<<1));
            for(int p=i<<1,j=0;j<N;j+=p)
            {
                int w=1;
                for(int k=0;k<i;++k,w=1ll*w*W%MOD)
                {
                    int X=P[j+k],Y=P[i+j+k]*1ll*w%MOD;
                    P[j+k]=(X+Y)%MOD;P[i+j+k]=(X-Y+MOD)%MOD;
                }
            }
        }
        if(opt==-1)
        {
            reverse(&P[1],&P[N]);
            for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
        }
    }
}
int b[MAX];
int A[MAX],B[MAX],C[MAX],D[MAX],c[MAX];
void Inv(int *a,int *b,int len)
{
    if(len==1){b[0]=fpow(a[0],MOD-2);return;}
    Inv(a,b,len>>1);
    for(int i=0;i<len;++i)A[i]=a[i],B[i]=b[i];
    NTT::NTT(A,len<<1,1);NTT::NTT(B,len<<1,1);
    for(int i=0;i<(len<<1);++i)A[i]=1ll*A[i]*B[i]%MOD*B[i]%MOD;
    NTT::NTT(A,len<<1,-1);
    for(int i=0;i<len;++i)b[i]=(b[i]+b[i])%MOD;
    for(int i=0;i<len;++i)b[i]=(b[i]+MOD-A[i])%MOD;
    for(int i=0;i<(len<<1);++i)A[i]=B[i]=0;
}
void Sqrt(int *a,int *b,int len)
{
    if(len==1){b[0]=a[0];return;}
    Sqrt(a,b,len>>1);
    for(int i=0;i<=len;++i)C[i]=a[i];
    Inv(b,D,len);
    NTT::NTT(C,len<<1,1);NTT::NTT(D,len<<1,1);
    for(int i=0;i<(len<<1);++i)D[i]=1ll*D[i]*C[i]%MOD;
    NTT::NTT(D,len<<1,-1);
    for(int i=0;i<len;++i)b[i]=1ll*(D[i]+b[i])%MOD*inv2%MOD;
    for(int i=0;i<=(len<<1);++i)C[i]=D[i]=0;
}
int main()
{
    n=read();m=read();inv2=fpow(2,MOD-2);
    for(int i=1;i<=n;++i)++d[read()];
    int N=1;while(N<=m)N<<=1;
    for(int i=0;i<N;++i)d[i]=(-4*d[i]+MOD)%MOD;
    ++d[0];
    Sqrt(d,c,N);
    for(int i=0;i<N;++i)d[i]=0;
    c[0]=(c[0]+1)%MOD;
    Inv(c,d,N);
    for(int i=0;i<=m;++i)d[i]=(d[i]+d[i])%MOD;
    for(int i=1;i<=m;++i)printf("%d\n",d[i]);
    return 0;

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值