數學推導題,NTT,快速數論變換,Wannafly-乒乓球
阿新 • • 發佈:2018-12-13
乒乓球
題目描述
小 是某省乒乓球名列前茅的選手,現在他有 n 顆乒乓球一字排開,第$i $顆乒乓球的權值為 每次他會隨機從現有的乒乓球中等概率選一顆拿走,然後得到的收益是這顆球左邊第一個乒乓球和右邊第一個乒乓球的權值的乘積,如果左邊沒有乒乓球或者右邊沒有乒乓球,則收益為 ,這個過程會重複進行到所有球都被拿走為止 現在小 想知道他的期望總收益 為了方便,你只需要輸出答案對 取模的值.
解決方案
思路就是列舉一對乒乓球然後計算它對於答案的貢獻. 要想產生貢獻,那麼至少要滿足.
考慮兩顆乒乓球一定要在之間的乒乓球全都取完才能取,這樣的情況共有
也就是,而最後的答案要除以,所以在這裡直接除掉就可以了,也就是
共線即為
而這個值只與的大小有關,而如果我們將式子中第二個反轉,即改寫成.(其中)
那麼
這下我們列舉,找所有滿足的對,使得
顯然這就是一個求卷積的裸題了,序列與序列卷積的第項就是我們要求的答案.
程式碼
#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;
}