UOJ658 【ULR #2】雜湊殺手 【q-analog,整式遞推】
阿新 • • 發佈:2022-05-27
模 \(p=998\,244\,353\) 意義下,給定 \(n-1\) 次多項式 \(f(x)\) 的點值 \(f(q^0),f(q^1),\cdots,f(q^{n-1})\) 以及非負整數 \(m\),求 \(m\) 次項係數。
\(m<n\le\text{ord}_p(q)\),\(f(q^i)\) 不超過 \(10^5\) 項非 \(0\)。
\[\begin{aligned} f(x)&=\sum_{i=0}^{n-1}\frac{f(q^i)}{(x-q^i)\prod_{j\ne i}(q^i-q^j)}\cdot\prod_{i=0}^{n-1}(x-q^i) \end{aligned} \]首先的問題是 \(\prod_{j\ne i}(q^i-q^j)\)
把這項與 \(f(q^i)\) 合併,設為 \(v_i\),然後為了貼近 q-analog 的形式翻轉一下係數:
\[f^\text R(x)=\sum_{i=0}^{n-1}\frac{v_i}{1-q^ix}\cdot\prod_{i=0}^{n-1}(1-q^ix) \]接下來就是純純推柿子了,取 \(m\)
初值顯然是 \(f_0=(-1)^mq^{\binom{m+1}2}\binom{n-1}m_q\)
這是整式遞推的形式,跟階乘一個做法:轉移式寫成 \(f_i=(Af_{i-1}+B)/C\),其中 \(A,B,C\) 是 \(q^i\) 的多項式,分塊轉移然後多點求值即可。
#include<bits/stdc++.h>
typedef long long LL;
const int B = 500, N = 1 << 21, mod = 998244353;
void qmo(int &x){x += x >> 31 & mod;}
int ksm(int a, int b){
int res = 1;
for(;b;b >>= 1, a = (LL)a * a % mod)
if(b & 1) res = (LL)res * a % mod;
return res;
}
int n, m, nb, k, q, qb, qbi, qm, qn, bin[B + 3]; // bin[i] = (-1)^i * q^{\binom{i+1}2} * \binom Bi_q
int rev[N], lim, w[N];
void calrev(int len){
int L = -1; lim = 1;
while(lim <= len){lim <<= 1; ++ L;}
for(int i = 0;i < lim;++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L);
}
void NTT(int *A, bool op){
for(int i = 0;i < lim;++ i) if(i < rev[i]) std::swap(A[i], A[rev[i]]);
for(int md = 1;md < lim;md <<= 1)
for(int i = 0;i < lim;i += md << 1)
for(int j = 0;j < md;++ j){
int y = (LL)A[md + i + j] * (op && j ? mod - w[(md << 1) - j] : w[md + j]) % mod;
qmo(A[md + i + j] = A[i + j] - y); qmo(A[i + j] += y - mod);
}
if(op){
int inv = ksm(lim, mod - 2);
for(int i = 0;i < lim;++ i) A[i] = (LL)A[i] * inv % mod;
}
}
int qw[N], qwi[N]; // QingWa ShiZhouXin
void CZT(int *a, int n, int m, int *ans){
static int A[N], B[N];
for(int i = 0;i <= n;++ i) A[n - i] = (LL)a[i] * qwi[i] % mod;
for(int i = 0;i < n + m;++ i) B[i] = qw[i];
NTT(A, 0); NTT(B, 0);
for(int i = 0;i < lim;++ i) A[i] = (LL)A[i] * B[i] % mod;
NTT(A, 1);
for(int i = 0;i < m;++ i) ans[i] = (LL)A[n + i] * qwi[i] % mod;
memset(A, 0, lim << 2);
memset(B, 0, lim << 2);
}
int ans[N], pre[N], anss[N], anst[N], ans1[N], ans2[N], prod[B + 3], deno[B + 3], coef[B + 3], tp[B + 3], Const;
int calfac(int n){
int res = pre[n / B];
for(int i = n / B * B + 1, tmp = ksm(q, i);i <= n;++ i, tmp = (LL)tmp * q % mod)
res = res * (mod + 1ll - tmp) % mod;
return res;
}
int calbin(int n, int m){
if(m < 0 || n < m) return 0;
return (LL)calfac(n) * ksm((LL)calfac(m) * calfac(n - m) % mod, mod - 2) % mod;
}
int calG(int i){
int res = (LL)calfac(n - i - 1) * calfac(i) % mod * ksm(q, (i * (2ll * n - 3 - i) >> 1) % (mod - 1)) % mod;
if(i & 1) res = mod - res;
return res;
}
int calF(int n){
int resx = anss[n / B], resy = anst[n / B];
for(int i = n / B * B + 1, tmp = ksm(q, i);i <= n;++ i, tmp = (LL)tmp * q % mod){
resx = ((LL)resx * qm % mod * (tmp - 1) + (LL)Const * resy) % mod;
resy = (LL)resy * (mod + tmp - qn) % mod;
}
return (LL)resx * ksm(resy, mod - 2) % mod;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin >> n >> m >> k >> q;
nb = n / B; calrev(B + nb);
for(int md = 1;md < N;md <<= 1){
w[md] = 1; int Wn = ksm(3, (mod - 1) / (md << 1));
for(int i = 1;i < md;++ i) w[md + i] = (LL)w[md + i - 1] * Wn % mod;
}
m = n - 1 - m;
qb = ksm(q, B); qbi = ksm(qb, mod - 2); qm = ksm(q, m); qn = ksm(q, n);
*bin = *qw = *qwi = *pre = *prod = *deno = *anst = 1;
for(int i = 1, tmp = 1;i <= B;++ i, tmp = (LL)tmp * q % mod)
bin[i] = (LL)bin[i - 1] * ksm(((LL)q * tmp - 1) % mod, mod - 2) % mod * q % mod * (tmp - qb + mod) % mod;
for(int i = 1, tmp = 1;i < N;++ i, tmp = (LL)tmp * qb % mod)
qw[i] = (LL)qw[i - 1] * tmp % mod;
for(int i = 1, tmp = 1;i < N;++ i, tmp = (LL)tmp * qbi % mod)
qwi[i] = (LL)qwi[i - 1] * tmp % mod;
CZT(bin, B, nb, ans + 1);
for(int i = 1;i <= nb;++ i) pre[i] = (LL)pre[i-1] * ans[i] % mod;
anss[0] = (LL)ksm(q, (m * (m + 1ll) >> 1) % (mod - 1)) * calbin(n - 1, m) % mod;
Const = (LL)ksm(q, (m * (m - 1ll) >> 1) % (mod - 1)) * calbin(n, m) % mod * (mod + qm - qn) % mod;
if(m & 1){anss[0] = mod - anss[0]; Const = mod - Const;}
for(int i = 1, tmp = q;i <= B;++ i, tmp = (LL)tmp * q % mod){
for(int j = 0;j < i;++ j){
qmo(tp[j] -= (LL)coef[j] * qm % mod);
tp[j + 1] = (tp[j + 1] + (LL)coef[j] * qm % mod * tmp) % mod;
tp[j] = (tp[j] + (LL)deno[j] * Const) % mod;
}
memcpy(coef, tp, (i + 1) << 2);
memset(tp, 0, (i + 1) << 2);
for(int j = 0;j < i;++ j){
qmo(tp[j] -= (LL)prod[j] * qm % mod);
tp[j + 1] = (tp[j + 1] + (LL)prod[j] * qm % mod * tmp) % mod;
}
memcpy(prod, tp, (i + 1) << 2);
memset(tp, 0, (i + 1) << 2);
for(int j = 0;j < i;++ j){
qmo(tp[j] -= (LL)deno[j] * qn % mod);
tp[j + 1] = (tp[j + 1] + (LL)deno[j] * tmp) % mod;
}
memcpy(deno, tp, (i + 1) << 2);
memset(tp, 0, (i + 1) << 2);
}
CZT(coef, B, nb, ans1 + 1);
CZT(deno, B, nb, ans2 + 1);
int qmb = ksm(qm, B); if(B & 1) qmb = mod - qmb;
for(int i = 1;i <= nb;++ i){
anss[i] = ((LL)qmb * ans[i] % mod * anss[i-1] + (LL)ans1[i] * anst[i-1]) % mod;
anst[i] = (LL)anst[i-1] * ans2[i] % mod;
}
int zuowanleaaaa = 0;
while(k --){
int pos, coe;
std::cin >> pos >> coe;
zuowanleaaaa = (zuowanleaaaa + (LL)calF(pos) * ksm(calG(pos), mod - 2) % mod * coe) % mod;
}
std::cout << zuowanleaaaa << '\n';
}