牛客国庆集训派对Day4 E 乒乓球(组合计数 + NTT)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/82941459

求这个期望,无非就是要求一个贡献。而这个贡献就是任意间隔大于二的两个数字的乘积,然后统计它们的出现次数。

对于两个位置i和j,满足j>i+1,现在考虑如何计算这个会出现的次数。我们令j-i-1=k,要做到会有wi*wj这个贡献,意味着从i和j中间k个乒乓球必须比第i和j个乒乓球先拿走。这个方案数我们这么考虑,首先着k个乒乓球可以任意排列,然后i和j两个放在它们后面,之后剩下n-k-2个乒乓球插入着k+2个乒乓球中,方案数就是k!*2*(k+3)*(k+4)*...*n。也即方案数是:

                                                               \large \frac{2*n!}{(k+1)*(k+2)}

那么我们再枚举这个i和j,则期望式子的分子可以写成:

                                           \large \sum_{i=1}^{n-2}\sum_{j=i+2}^{n}w_i*w_j*\frac{2*n!}{(j-i-1)*(j-i)}

我们令k=j-i,那么有:

                                          \large 2*n!*\sum_{k=2}^{n-1}\sum_{j=k+1}^{n}w_{j-k}*w_j*\frac{1}{(k-1)*k}

交换一下求和次序:

                                             \large 2*n!*\sum_{j=3}^{n}w_j*\sum_{k=1}^{n-2}w_{j-k}*\frac{1}{(k-1)*k}

如果我们令g(k)=1/((k-1)*k),那么这个期望式子的分子就可以写成卷积的形式:

                                            \large 2*n!*\sum_{j=3}^{n}w_j*\sum_{k=1}^{n-2}w_{j-k}*g(k)

我们对于右边那个sigma用NTT优化,然后最后O(N)的求和就可以算出期望的分子,总的复杂度O(NlogN)。最后再除以分母n!即可。具体见代码:

#include<bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define mod 998244353
#define LL long long
#define N 550010
using namespace std;

int inv[N],fac[N],ifac[N];
int c[N],d[N],a[N],n,ans;

int qpow(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1)ans=(LL)ans*a%mod;
        a=(LL)a*a%mod; b>>=1;
    }
    return ans;
}

namespace NTT
{
    #define g 3
    int x[N<<2],y[N<<2],wn[N<<2];

    void init()
    {
        for(int i=0;i<21;i++)
        {
            int t=1<<i; wn[i]=qpow(g,(mod-1)/t);
        }
    }

    void brc(int *F,int len)
    {
        int j=len/2;
        for(int i=1;i<len-1;i++)
        {
            if(i<j)swap(F[i],F[j]);
            int k=len/2;
            while(j>=k) j-=k,k>>=1;
            if(j<k)j+=k;
        }
    }

    void NTT(int *F,int len,int t)
    {
        int id=0; brc(F,len);
        for(int h=2;h<=len;h<<=1)
        {
            id++;
            for(int j=0;j<len;j+=h)
            {
                int E=1;
                for(int k=j;k<j+h/2;k++)
                {
                    int u=F[k],v=(LL)E*F[k+h/2]%mod;
                    F[k]=(u+v)%mod,F[k+h/2]=((u-v)%mod+mod)%mod;
                    E=(LL)E*wn[id]%mod;
                }
            }
        }
        if(t==-1)
        {
            for(int i=1;i<len/2;i++)swap(F[i],F[len-i]);
            LL inv=qpow(len,mod-2);
            for(int i=0;i<len;i++)F[i]=(LL)F[i]%mod*inv%mod;
        }
    }

    void multiply(int *a,int len1,int *b,int len2)
    {
        int len=1;
        while(len<len1+len2)len<<=1;
        for (int i = len1; i < len; i++) a[i] = 0;
        for (int i = len2; i < len; i++) b[i] = 0;
        NTT(a,len,1); NTT(b,len,1);
        for(int i=0;i<len;i++) a[i]=(LL)a[i]*b[i]%mod;
        NTT(a,len,-1);
    }
}


void init()
{
    fac[0]=ifac[0]=inv[0]=1;
    fac[1]=ifac[1]=inv[1]=1;
    for(int i=2;i<N;i++)
    {
        fac[i]=fac[i-1]*(LL)i%mod;
        inv[i]=(mod-mod/i)*(LL)inv[mod%i]%mod;
        ifac[i]=ifac[i-1]*(LL)inv[i]%mod;
    }
}

int main()
{
    IO; init(); cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    int len=1;while(len<n*2+10) len<<=1;
    for(int i=1;i<=len;i++) c[i]=d[i]=0;
    for(int i=2;i<=n-1;i++)
        c[i]=(LL)inv[i]*inv[i+1]%mod;
    for(int i=2;i<=n-1;i++) d[n-i]=a[n-i];
    NTT::init();
    NTT::multiply(c,len,d,len);
    for(int i=3;i<=n;i++)
        ans=(ans+(LL)c[i]*a[i]%mod)%mod;
    cout<<(LL)ans*2%mod<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/82941459