数学推导题,NTT,快速数论变换,Wannafly-乒乓球

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

乒乓球

题目描述

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

解决方案

思路就是枚举一对乒乓球 ( i , j ) (i,j) 然后计算它对于答案的贡献.
( i , j ) (i,j) 要想产生贡献,那么至少要满足 2 j i n 1 2 \le j-i \le n-1 .

考虑 i , j i,j 两颗乒乓球一定要在 [ i + 1 , j 1 ] [i+1,j-1] 之间的乒乓球全都取完才能取,这样的情况共有 ( j i 1 ) ! 2 ! C n j i + 1 ( n ( j i + 1 ) ) ! (j-i-1)!*2!*C_n^{j-i+1}*(n-(j-i+1))!

也就是 2 n ! ( j i ) ( j i + 1 ) \frac{2*n!}{(j-i)(j-i+1)} ,而最后的答案要除以 n ! n! ,所以在这里直接除掉就可以了,也就是 2 ( j i ) ( j i + 1 ) \frac{2}{(j-i)(j-i+1)}

共线即为 w i w j 2 ( j i ) ( j i + 1 ) w_i*w_j*\frac{2}{(j-i)(j-i+1)}

而这个值只与 ( j i ) (j-i) 的大小有关,而如果我们将式子中第二个 w j w_j 反转,即改写成 b n j b_{n-j} .(其中 b i = w n i b_i = w_{n-i} )

那么 w i b n j 2 ( j i ) ( j i + 1 ) w_i*b_{n-j}*\frac{2}{(j-i)(j-i+1)}

这下我们枚举 2 ( t ) ( t + 1 ) \frac{2}{(t)(t+1)} ,找所有满足的 ( i , n j ) (i,n-j) 对,使得 n j i = n t n-j-i = n-t

显然这就是一个求卷积的裸题了, w w 序列与 b b 序列卷积的第 n t n-t 项就是我们要求的答案.

代码

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
typedef long long LL;
const int N = 1 << 20;
const int P = 998244353;
const int G = 3;
const int NUM = 20;

LL  wn[NUM];
LL  a[N], b[N];

LL quick_mod(LL a, LL b, LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % m;
            b--;
        }
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

void GetWn()
{
    for(int i = 0; i < NUM; i++)
    {
        int t = 1 << i;
        wn[i] = quick_mod(G, (P - 1) / t, P);
    }
}
void Rader(LL a[], int len)
{
    int j = len >> 1;
    for(int i = 1; i < len - 1; i++)
    {
        if(i < j) swap(a[i], a[j]);
        int k = len >> 1;
        while(j >= k)
        {
            j -= k;
            k >>= 1;
        }
        if(j < k) j += k;
    }
}

void NTT(LL a[], int len, int on)
{
    Rader(a, 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 = a[k] % P;
                LL t = w * a[k + h / 2] % P;
                a[k] = (u + t) % P;
                a[k + h / 2] = (u - t + P) % P;
                w = w * wn[id] % P;
            }
        }
    }
    if(on == -1)
    {
        for(int i = 1; i < len / 2; i++)
            swap(a[i], a[len - i]);
        LL inv = quick_mod(len, P - 2, P);
        for(int i = 0; i < len; i++)
            a[i] = a[i] * inv % P;
    }
}

void Conv(LL a[], LL b[], int n)
{
    NTT(a, n, 1);
    NTT(b, n, 1);
    for(int i = 0; i < n; i++)
        a[i] = a[i] * b[i] % P;
    NTT(a, n, -1);
}
LL Fac[N];
int n;
int main()
{	
	Fac[0] = 1;
	for(int i = 1;i < N;++i) {
		Fac[i] = Fac[i-1] * i % P;
	}
    GetWn();
	std::cin >> n;
	for(int i = 0;i < n;++i){ 
		std::cin >> a[i];
		b[n-i] = a[i];
	}
	int len = 1;
	while(len < n) len <<= 1;
	len <<= 1;
	Conv(a,b,len);
	LL ans = 0;
	for(int le = 2;le <= n-1;++le) {
		LL part = 2 * quick_mod(le,P-2,P) % P * quick_mod(le+1,P-2,P) % P;
		ans = (ans + (a[n-le]*part % P)) % P;
	}
	std::cout << ans << std::endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_37517391/article/details/82956787