UOJ658 【ULR #2】哈希杀手 【q-analog,整式递推】
模 \(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)\) 仅有 \(k\le 10^5\) 项非 \(0\)。
推荐阅读:ARC139F
\[\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)\) 怎么算?先拆成 \(\prod_{j=1}^i(1-q^j)\),然后套用经典的分块方法:待定块长 \(B\),\(\prod_{j=1}^B(1-q^jx)\) 的系数就是组合数,然后用 Chirp-Z 变换算出 \(q^0,q^B,\cdots,q^{\lfloor n/B\rfloor B}\) 处的点值。时间复杂度 \(\mathcal O(n/B\cdot\log n+k\cdot B)=\mathcal O(\sqrt{kn\log n})\)。
把这项与 \(f(q^i)\) 合并,设为 \(v_i\),然后为了贴近 q-二项式系数的形式翻转一下系数:
\[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\) 次项系数,对后一项代入组合数的柿子,对前一项的分母展开,然后利用吸收恒等式写出递推式:
\[\begin{aligned}
f_i&=q^{im}\sum_{j=0}^m(-1)^jq^{-ij+\binom j2}\binom nj_q \\
&=q^{im}\left(\sum_{j=1}^m(-1)^jq^{-ij+\binom j2}(1-q^j)\binom nj_q+\sum_{j=0}^m(-1)^jq^{-(i-1)j+\binom j2}\binom nj_q\right) \\
&=q^{im}\left(q^{n-i}\sum_{j=0}^{m-1}(-1)^jq^{-ij+\binom{j}2}\binom n{j}_q-q^{-i}\sum_{j=0}^{m-1}(-1)^jq^{-(i-1)j+\binom{j}2}\binom n{j}_q+\sum_{j=0}^m(-1)^jq^{-(i-1)j+\binom j2}\binom nj_q\right) \\
&=q^{n-i}\left(f_i-(-1)^mq^{\binom m2}\binom nm_q\right)-q^{m-i}\left(f_{i-1}-(-1)^mq^{\binom m2}\binom nm_q\right)+q^mf_{i-1} \\
(q^i-q^n)f_i&=(q^{m+i}-q^m)f_{i-1}+(q^m-q^n)\cdot(-1)^mq^{\binom m2}\binom nm_q
\end{aligned}
\]
初值显然是 \(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';
}