牛客国庆集训派对Day4-E-乒乓球(NTT)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haut_ykc/article/details/82990575

链接:https://www.nowcoder.com/acm/contest/204/E
来源:牛客网
 

题目描述

小 Bo 是某省乒乓球名列前茅的选手,现在他有 n 颗乒乓球一字排开,第 i 颗乒乓球的权值为 wi
每次他会随机从现有的乒乓球中等概率选一颗拿走,然后得到的收益是这颗球左边第一个乒乓球和右边第一个乒乓球的权值的乘积,如果左边没有乒乓球或者右边没有乒乓球,则收益为 0,这个过程会重复进行到所有球都被拿走为止
现在小 Bo 想知道他的期望总收益
为了方便,你只需要输出答案对 998244353 取模的值

输入描述:

第一行一个正整数 n
第二行 n 个正整数 w1…wn

输出描述:

输出答案对 998244353 取模的值

示例1

输入

复制

3
1 1 1

输出

复制

332748118

说明

答案是 

备注:

1≤ n≤ 105
1≤ wi≤ 107

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long 
#define mod 998244353
ll a[300005],b[300005],n,ans;
ll wn[30];
ll q(ll x,ll y)
{
	ll res=1;x%=mod;
	while(y)
	{
		if(y%2)
			res=res*x%mod;
		x=x*x%mod;
		y/=2;
	}
	return res;
}
void getwn()
{
	for(int i=0;i<20;i++)
	{
		int  tmp=1<<i;
		wn[i]=q(3ll,(mod-1)/tmp);
	}
}
void change(ll y[],int len)
{
	int i,j,k;
	for(i=1,j=len/2;i<len-1;i++)
	{
		if(i<j)            
			swap(y[i],y[j]);
		k=len/2;
		while(j>=k)
			j-=k,k/=2;
		if(j<k)
			j+=k;  
	}
}
void ntt(ll y[], int len, int on) 
{
    change(y, len); int id=0;
	for(int h=2;h<=len;h<<=1)
	{
		id++;
		for(int j=0;j<len;j+=h)
		{
			ll w=1;
			for(int k=j;k<j+h/2;k++)
			{
				ll u=y[k];
				ll t=w*y[k+h/2]%mod;
				y[k]=(u+t)%mod;
				y[k+h/2]=(u-t+mod)%mod;
				w=w*wn[id]%mod;
			}
		}
	}
	if(on==-1)
	{
		ll inv=q(len,mod-2);
		for(int i=1;i<len/2;i++)
			swap(y[i],y[len-i]);
		for(int i=0;i<len;i++)
			y[i]=y[i]*inv%mod;
	}
}
int main(void)
{
	getwn();
	scanf("%lld",&n);
	for(int i=0;i<n;i++)
		scanf("%lld",&a[i]);
	for(int i=0;i<n;i++)
		b[n-i-1]=a[i];
	int len=1;
	while(len<=n*2) 
		len*=2;
	ntt(a,len,1);ntt(b,len,1);
	for(int i=0;i<len;i++)
		a[i]=a[i]*b[i]%mod;
	ntt(a,len,-1);
	for(int i=2;i<n;i++)
		ans=(ans+2ll*a[n+i-1]*q((ll)i*(i+1),mod-2)%mod)%mod;
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/haut_ykc/article/details/82990575