The 2020 ICPC Asia Macau Regional Contest A. Accelerator(分治+NTT)

本文介绍了一种高效计算多项式乘法的方法,利用快速幂和线性基理论,实现了在大规模整数模意义下计算指定指数系数的高效算法。通过实例展示了如何初始化、计算和操作线性基,并应用于解决实际问题,如计算特定指数的乘积之和。
摘要由CSDN通过智能技术生成

A. Accelerator

实质上就是求n个多项式相乘
( a 1 + x ) ( a 2 + x ) … ( a n + x ) (a_1+x)(a_2+x)\dots(a_n+x) (a1+x)(a2+x)(an+x)
对于 [ x m ] [x^m] [xm]的系数即是从 n n n个选出 n − m n-m nm的乘积之和。

每次从 n n n个选出 n − m n-m nm的概率显然为
( n m ) n ! \frac{\binom{n}{m}}{n!} n!(mn)

然后期望累加即可。


或者说求
( 1 + a 1 x ) ( 1 + a 2 x ) … ( 1 + a n x ) (1+a_1x)(1+a_2x)\dots(1+a_nx) (1+a1x)(1+a2x)(1+anx)
对于 [ x m ] [x^m] [xm]的系数即是从 n n n个选出 m m m的乘积之和。

#include<bits/stdc++.h>

using namespace std;
using ll=long long;

const int N=300010;
const int P=998244353;
const int G=3,Gi=332748118;
const int mod=998244353;

int qmi(int a,int b)
{
    int v=1;
    while(b)
    {
        if(b&1) v=1ll*v*a%P;
        a=1ll*a*a%P;
        b>>=1;
    }
    return v;
}
int rev[N];
void NTT(int *a,int n,int inv)
{
    for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    
    for(int mid=1;mid<n;mid<<=1)
    {
        int Wn=qmi((inv==1)?G:Gi,(P-1)/(mid<<1));
        for(int i=0;i<n;i+=(mid<<1))
        {
            int w=1;
            for(int j=0;j<mid;j++,w=1ll*w*Wn%P)
            {
                int x=a[i+j],y=1ll*w*a[i+j+mid]%P;
                a[i+j]=(x+y)%P;
                a[i+j+mid]=(x-y+P)%P;
            }
        }
        
    }
    if(inv==-1) 
    {
        int invn=qmi(n,P-2);
        for(int i=0;i<n;i++) a[i]=1ll*a[i]*invn%P;
    }
}
//=======================================================
int fact[N],infact[N];
void init()
{
    fact[0]=1;
    for(int i=1;i<=100000;i++) fact[i]=1ll*i*fact[i-1]%mod;
    infact[100000]=qmi(fact[100000],mod-2);
    
    for(int i=99999;i;i--) infact[i]=1ll*infact[i+1]*(i+1)%mod;
}
int n,A[N],B[N],C[N];
int Mul(int *a,int *b,int n,int m,int *ans)
{
    int bit=0,num=1;
    while(num<n+m+1) num<<=1,bit++;
    
    for(int i=0;i<num;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
    for(int i=0;i<=n;i++) A[i]=a[i];
    for(int i=0;i<=m;i++) B[i]=b[i];
    for(int i=n+1;i<num;i++) A[i]=0;
    for(int i=m+1;i<num;i++) B[i]=0;
    NTT(A,num,1);NTT(B,num,1);
    for(int i=0;i<num;i++) C[i]=1ll*A[i]*B[i]%mod;
    NTT(C,num,-1);
    for(int i=0;i<=n+m;i++) ans[i]=C[i];
    return n+m;
}

int pool[N<<1],tot;
struct Node
{
    int *p,n;
    void init(int x)
    {
        this->n=1;
        p=pool+tot;
        for(int i=0;i<=n;i++) p[i]=0;
        p[0]=x;p[1]=1;
        tot+=n+1;
    }
    void mul(const Node&o)
    {
        n=Mul(p,o.p,n,o.n,p);
    }
};
Node solve(int l,int r)
{
    Node ans;
    if(l==r)
    {
        int x;
        cin>>x;
        ans.init(x);
        return ans;
    }
    int mid=l+r>>1;
    ans=solve(l,mid);
    ans.mul(solve(mid+1,r));
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    init();
    int Tc;
    cin>>Tc;
    while(Tc--)
    {
        cin>>n;
        Node res=solve(1,n);
        
        ll ans=0;
        for(int i=1;i<=n;i++)
            ans=(ans+1ll*res.p[n-i]*fact[i]%mod*fact[n-i]%mod)%mod;
        ans=ans*infact[n]%mod;
        printf("%lld\n",ans);
    }
    return 0;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值