其他分享
首页 > 其他分享> > [学习笔记] 单位根反演

[学习笔记] 单位根反演

作者:互联网

引入

单位根反演一般用于求一类 \(i \bmod k\) 的求和式,通过枚举 \(j \equiv i \pmod{k}\),将式子转化为 \(k\) 次单位根下的操作。这一般要求 \(k \mid (\mathrm{mod}-1)\)。通常会结合二项式定理使用。

单位根反演

在 FFT 中我们其实已经见过它了:

\[[n\mid k] = \frac{1}{n} \sum_{i=0}^{n-1} \omega_n^{ki} \]

让我们来证明一下:

容易得到如下推论:若 \(p \equiv q \pmod{n}\),即 \(p - q \equiv 0 \pmod{n}\),那么有:

\[[n \mid p-q] = \frac{1}{n} \sum_{i=0}^{n-1} \omega_n^{pi} \omega_n^{-qi} \]

求多项式特定倍数的系数和

\[\sum_{i=0}^{\lfloor \frac{n}{k} \rfloor} [x^{ik}] f(x) = \frac{1}{k} \sum_{j=0}^{k-1} f(\omega_k^j) \]

把多项式大力展开后运用单位根反演容易证明上式。改变求和顺序,上式也可以写成较为简洁的形式:

\[\sum_{k|n} [x^n] f(x) = \frac{1}{k} \sum_{i=0}^{k-1} f(\omega_k^i) \]

事实上上式与 CRT 和 Lagrange 插值公式具有奇妙的联系,但这和本文主题没有太大关系,因此在这里不做深入展开。不过因为它是实在是很美妙,大概还是会专门开一篇博客讲讲这个东西。

LOJ 6485 LJJ 学二项式定理

\[\sum_{i=0}^n C_{n}^i s^i a_{i \bmod 4} = \sum_{i=0}^n C_n^i s^i \sum_{j=0}^3 a_j [i \equiv j \ (\mathrm{mod} \ 4)] \]

根据推论,上式可化简为:

\[\begin{aligned} \frac{1}{4} \sum_{i=0}^n C_n^i s^i \sum_{j=0}^3 \sum_{k=0}^3 \omega_4^{ik} \omega_4^{-jk} &= \frac{1}{4} \sum_{j=0}^3 a_j \sum_{k=0}^3 \omega_4^{-jk} \sum_{i=0}^n C_n^i s^i \omega_4^{ik} \\ &= \frac{1}{4} \sum_{j=0}^3 a_j \sum_{k=0}^3 \omega_4^{-jk} (s \omega_4^k +1) ^n \end{aligned} \]

众所周知 \(998244353\) 的原根是 \(3\),因此 \(\omega_4^1 = 3\)。预处理单位根及其逆元即可做到 \(O(T \log n)\)。

Code
/*
最黯淡的一个 梦最为炽热
万千孤单焰火 让这虚构灵魂鲜活
至少在这一刻 热爱不问为何
存在为将心声响彻
*/
#include <bits/stdc++.h>
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define int long long
#define mem(x, v, n) memset(x, v, sizeof(int) * (n))
#define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n))
#define lob lower_bound
#define upb upper_bound
using namespace std;

inline int read() {
	int x = 0, w = 1;char ch = getchar();
	while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); }
	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * w;
}

inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }

const int MN = 2e5 + 5;
const int Mod = 998244353;

inline void Pls(int &x, int y) { x += y; if (x >= Mod) x -= Mod; }
inline void Dec(int &x, int y) { x -= y; if (x < 0) x += Mod; }

inline int qPow(int a, int b = Mod - 2, int ret = 1) {
    while (b) {
        if (b & 1) ret = ret * a % Mod;
        a = a * a % Mod, b >>= 1;
    }
    return ret;
}

// #define dbg

int N, s, w[5], iw[5];

inline void Work() {
    N = read(), s = read();
    int ans = 0;
    for (int i = 0; i < 4; i++) {
        int x = read();
        int cur = 1, sig = 0;
        for (int j = 0; j < 4; j++) {
            sig = (sig + cur * qPow(s * w[j] % Mod + 1, N) % Mod) % Mod;
            cur = cur * iw[i] % Mod;
        }
        ans = (ans + x * sig % Mod) % Mod;
    }
    ans = ans * qPow(4) % Mod;
    printf("%lld\n", ans);
}

signed main(void) {
    w[0] = 1;
    w[1] = qPow(3, (Mod - 1) / 4);
    w[2] = w[1] * w[1] % Mod;
    w[3] = w[1] * w[2] % Mod;
    for (int i = 0; i <= 3; i++) iw[i] = qPow(w[i]);

    int T = read();
    while (T--) Work();
    return 0;
}

标签:frac,int,sum,单位根,笔记,反演,define,omega,Mod
来源: https://www.cnblogs.com/came11ia/p/16498145.html