【LOJ 6485】LJJ 学二项式定理(单位根反演)(模板)
LJJ 学二项式定理
题目链接:LOJ 6485
题目大意
求一个式子:
\(\sum\limits_{i=0}^n(\binom{n}{i}s^ia_{i\bmod 4})\)
其中 \(n\leq 10^{18}\)
思路
这道题可以算是单位根反演的模板题。
单位根反演
首先单位根反演是用来处理一下带有取模的式子的。
(就系数里面有取模可以用这个弄掉)
然后式子是 \([n|a]=\dfrac{1}{n}\sum\limits_{k=0}^{n-1}\omega_n^{ak}\)
然后给证明:
若 \(a\neq 0(\bmod\ n)\)
那我们可以用等比数列求和:
\(\dfrac{1}{n}\dfrac{\omega_n^{an}-1}{\omega_n^a-1}\)
然后 \(\omega^a_n\neq 1,\omega_n^a-1\neq 0\) 分母没问题,分子因为 \(\omega_n^n=\omega_n^0=1\),\(\omega^{an}_n=(\omega_n^n)^a=1,\omega_n^{an}-1=0\)
若 \(a=0(\bmod\ n)\)
那特殊处理(因为这个时候下面是 \(0\))
\(\dfrac{1}{n}\sum\limits_{k=0}^{n-1}\omega_n^{ak\bmod n}\)
\(\dfrac{1}{n}\sum\limits_{k=0}^{n-1}\omega_n^{0}=\dfrac{1}{n}n=1\)
然后一个经典的式子:
\([a=b\pmod n]=[a-b=0\pmod n]=[n|(a-b)]\)
\(=\dfrac{1}{n}\sum\limits_{k=0}^{n-1}\omega_n^{(a-b)k}=\dfrac{1}{n}\sum\limits_{k=0}^{n-1}\omega_n^{ak}\omega_n^{-bk}\)
这道题
\(\sum\limits_{i=0}^n(\binom{n}{i}s^ia_{i\bmod 4})\)
\(\sum\limits_{i=0}^n(\binom{n}{i}s^i(\sum\limits_{j=0}^3a_j[i\bmod 4=j]))\)
\(\sum\limits_{i=0}^n(\binom{n}{i}s^i(\sum\limits_{j=0}^3a_j[4|i-j]))\)
\(\sum\limits_{i=0}^n(\binom{n}{i}s^i(\sum\limits_{j=0}^3a_j(\dfrac{1}{4}\sum\limits_{k=0}^{3}\omega_4^{ik}\omega_4^{-jk})))\)
\(\dfrac{1}{4}\sum\limits_{k=0}^{3}(\sum\limits_{j=0}^3a_j\omega_4^{-jk})(\sum\limits_{i=0}^n\binom{n}{i}s^i\omega_4^{ik})\)
(二项式反演)
\(\sum\limits_{i=0}^n\binom{n}{i}s^i\omega_4^{ik}\)
\(\sum\limits_{i=0}^n\binom{n}{i}(s\omega_4^{k})^i1^{n-i}\)
\((s\omega_4^k+1)^n\)
(带回原式)
\(\dfrac{1}{4}\sum\limits_{k=0}^{3}(\sum\limits_{j=0}^3a_j\omega_4^{-jk})(s\omega_4^k+1)^n\)
然后 \(\omega_4^1=g^{(mod-1)/4}\)。
代码
#include<cstdio>
#define ll long long
#define mo 998244353
using namespace std;
ll n, s, a[4], G = 3, Gv, v4, ans, w[4];
ll ksm(ll x, ll y) {ll re = 1; while (y) {if (y & 1) re = re * x % mo; x = x * x % mo; y >>= 1;} return re;}
int main() {
int T; scanf("%d", &T); Gv = ksm(G, mo - 2); v4 = ksm(4, mo - 2);
w[0] = 1; w[1] = ksm(G, (mo - 1) / 4); for (int i = 2; i < 4; i++) w[i] = w[i - 1] * w[1] % mo;
while (T--) {
scanf("%lld %lld", &n, &s); for (int i = 0; i < 4; i++) scanf("%lld", &a[i]);
ans = 0;
for (int k = 0; k <= 3; k++) {
ll now = 0;
for (int j = 0; j <= 3; j++)
(now += a[j] * w[(4 - j) * k % 4] % mo) %= mo;
(ans += now * ksm((s * w[k] % mo + 1) % mo, n) % mo) %= mo;
}
printf("%lld\n", ans * v4 % mo);
}
return 0;
}