1. 程式人生 > >數學推導題,NTT,快速數論變換,Wannafly-乒乓球

數學推導題,NTT,快速數論變換,Wannafly-乒乓球

乒乓球

題目描述

BoBo 是某省乒乓球名列前茅的選手,現在他有 n 顆乒乓球一字排開,第$i $顆乒乓球的權值為 wiw_i 每次他會隨機從現有的乒乓球中等概率選一顆拿走,然後得到的收益是這顆球左邊第一個乒乓球和右邊第一個乒乓球的權值的乘積,如果左邊沒有乒乓球或者右邊沒有乒乓球,則收益為 00,這個過程會重複進行到所有球都被拿走為止 現在小 BoBo 想知道他的期望總收益 為了方便,你只需要輸出答案對 998244353998244353 取模的值.

解決方案

思路就是列舉一對乒乓球(i,j)(i,j)然後計算它對於答案的貢獻. (i,j)(i,j)

要想產生貢獻,那麼至少要滿足2jin12 \le j-i \le n-1.

考慮i,ji,j兩顆乒乓球一定要在[i+1,j1][i+1,j-1]之間的乒乓球全都取完才能取,這樣的情況共有(ji1)!2!Cnji+1(n(ji+1))!(j-i-1)!*2!*C_n^{j-i+1}*(n-(j-i+1))!

也就是2n!(ji)(ji+1)\frac{2*n!}{(j-i)(j-i+1)},而最後的答案要除以n!n!

,所以在這裡直接除掉就可以了,也就是2(ji)(ji+1)\frac{2}{(j-i)(j-i+1)}

共線即為wiwj2(ji)(ji+1)w_i*w_j*\frac{2}{(j-i)(j-i+1)}

而這個值只與(ji)(j-i)的大小有關,而如果我們將式子中第二個wjw_j反轉,即改寫成bnjb_{n-j}.(其中bi=wnib_i = w_{n-i})

那麼wibnj2(ji)(ji+1)w_i*b_{n-j}*\frac{2}{(j-i)(j-i+1)}

這下我們列舉2(t)(t+1)\frac{2}{(t)(t+1)},找所有滿足的(i,nj)(i,n-j)對,使得nji=ntn-j-i = n-t

顯然這就是一個求卷積的裸題了,ww序列與bb序列卷積的第ntn-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;
}