[JZOJ5666]【GDOI2018Day2模拟4.18】法力风暴(分治NTT 模板)

本文探讨了一个关于数学期望的问题,并通过引入指数型生成函数的方法来解决。问题涉及到一系列操作后的期望值计算,通过将问题转化为生成函数的形式,利用分治NTT等技巧实现了高效的求解。

Description

这里写图片描述
2n105,0Ai,k1092≤n≤105,0≤Ai,k≤109

Solution

注意到一次操作打出的伤害就是原来A的乘积减去操作后A的乘积

那么题目转化为求原来A的乘积减去最终A的乘积的期望

a[i]a[i]最终被减去了b[i]b[i]

那么最终期望为

E((A[i]b[i]))=1nkb[i]=k(k!b[i]!×(A[i]b[i]))E(∏(A[i]−b[i]))=1nk∑∑b[i]=k(k!∏b[i]!×∏(A[i]−b[i]))

我们可以把k!nkk!nk提出来,对每个i单独考虑
就变成
k!nkb[i]=k((A[i]b[i])b[i]!)k!nk∑∑b[i]=k(∏(A[i]−b[i])b[i]!)

设指数型生成函数

Fi(x)=j0(A[i]j)xjj!Fi(x)=∑j≥0(A[i]−j)xjj!

=j0A[i]xjjxjj!=j0(A[i]xjj!+x×xj1(j1)!)=∑j≥0A[i]xj−jxjj!=∑j≥0(A[i]xjj!+x×xj−1(j−1)!)

注意到j0xjj!=ex∑j≥0xjj!=ex

因为我们不关心整个序列是否收敛,我们关心的是具体项的系数
那么原式

=(A[i]x)ex=(A[i]−x)ex

把所有i乘起来

enx(A[i]x)enx∏(A[i]−x)

现在要求的就是这个多项式xkxk项的系数

F(x)=(A[i]x),G(x)=enx(A[i]x)F(x)=∏(A[i]−x),G(x)=enx∏(A[i]−x)

F(x)F(x)可以用分治NTT处理,次数是n

我们要求的是G(x)G(x)xkxk这一项的系数
暴力卷积
G(x)[xk]=k!nkj=0nnkj(kj)!×F(x)[xj]G(x)[xk]=k!nk∑j=0nnk−j(k−j)!×F(x)[xj]

复杂度O(Nlog2N)O(Nlog2⁡N)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 262144
#define LL long long
#define mo 998244353
using namespace std;
LL wi[2*N+5],a[2*N+5],ny[2*N+5],wg[2*N+5],a1[2*N+10],c[2*N+5],d[2*N+5];
int bit[2*N+5],n,m,L,fi[N],le[N],l2[N+5];
void prp(int num)
{
    fo(i,0,num-1) bit[i]=(bit[i>>1]>>1)|((i&1)<<(L-1));
    fo(i,0,num) wi[i]=wg[i*(N/num)];
}
inline LL md(LL x)
{
    return(x<0)?(x+mo):((x>=mo)?x-mo:x);
}
void NTT(LL *a,int pd,int num)
{
    prp(num);
    fo(i,0,num-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
    int lim=num>>1,half=1;
    LL v;
    for(int m=2;m<=num;half=m,m<<=1,lim>>=1)
    {
        fo(i,0,half-1)
        {
            LL w=(pd==1)?wi[i*lim]:wi[num-i*lim];
            for(int j=i;j<num;j+=m)
            {
                v=(w*a[j+half])%mo;
                a[j+half]=md(a[j]-v);
                a[j]=md(a[j]+v);
            }
        }
    }
    if(pd<0) fo(i,0,num-1) a[i]=a[i]*ny[num]%mo;
}
void doit(int l,int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    doit(l,mid),doit(mid+1,r);
    L=l2[le[l]+le[mid+1]];
    int rm=1<<L;
    fo(j,0,rm-1) 
    {
        c[j]=(j<=le[l]-1)?a1[fi[l]+j]:0;
        d[j]=(j<=le[mid+1]-1)?a1[fi[mid+1]+j]:0;
    }  
    NTT(c,1,rm),NTT(d,1,rm);
    fo(j,0,rm) c[j]=c[j]*d[j]%mo;
    NTT(c,-1,rm);
    le[l]=le[l]+le[mid+1]-1;
    fo(j,0,le[l]-1) a1[fi[l]+j]=c[j];
}
LL ksm(LL k,LL n)
{
    LL s=1;
    for(;n;k=k*k%mo,n>>=1) if(n&1) s=s*k%mo;
    return s;
}
int main()
{
    cin>>n>>m;
    LL s1=1;
    fo(i,1,n) scanf("%lld",&a[i]),a1[2*(i-1)]=a[i],a1[2*i-1]=998244352,fi[i]=2*(i-1),le[i]=2,s1=s1*a[i]%mo; 
    l2[1]=0;
    fo(i,1,18) l2[1<<i]=i;
    fod(i,N,1) if(!l2[i]) l2[i]=l2[i+1];
    LL vw=ksm(3,3808);
    ny[0]=wg[0]=1;
    fo(i,1,N) wg[i]=wg[i-1]*vw%mo,ny[i]=ksm(i,mo-2);
    doit(1,n);
    LL s=1,ans=0,yn=ksm(ksm(n,mo-2),m);
    fo(i,0,n)
    {
        if(i>m) break;
        ans=(ans+ksm(n,m-i)*s%mo*a1[i]%mo)%mo;
        s=s*(m-i)%mo;
    }
    ans=ans*yn%mo;
    printf("%lld\n",(s1-ans+mo)%mo);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值