1. 程式人生 > 其它 >UOJ658 【ULR #2】雜湊殺手 【q-analog,整式遞推】

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)\) 不超過 \(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)\)

怎麼算?先拆成 \(\prod_{j=1}^k(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-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\)

次項係數,對後一項代入組合數的柿子,對前一項的分母展開,然後利用吸收恆等式寫出遞推式:

\[\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';
}