题意非常的简单:求 对 取模
拿到这个式子,实在是没有忍住,一口气化了下去:
首先有 ,其中 表示 和 的最大公约数。这个结论只要把欧拉函数写成 就比较显然了。
枚举
:
莫比乌斯反演
令
那么
设函数
显然可以
预处理
再设函数
那么原式写成
可是想了想,
似乎没有很好的前缀和预处理方法
这时候看了一眼数据范围:多测
,
这谁顶得住啊???
于是我去翻了题解。不得不说还是有点毒瘤的。
首先注意到所有需要用到的
都满足
,所以可以用
把它们全部预处理出来存好,借助这个简单的递推公式:
我们可以设前缀和
,显然有递推关系
然后我们装个B设一个B,预处理所有
的
,同样用
存。注意在预处理的时候
,因此这个动态数组空间复杂度
可以接受
那么在回答询问的时候,枚举
,对于
的情形直接暴力,对于大于
的部分利用预处理的前缀和
整除分块计算
单个询问的时间复杂度就是
,预处理时间复杂度
,取
可以取得比较好的复杂度。
注意科学取模
#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>
#include <vector>
template <typename T> inline void read(T& x) {
int f = 0, c = getchar(); x = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
if (f) x = -x;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + 48);
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; }
typedef long long LL;
const LL mod = 998244353;
const int maxn = 1e5 + 207;
const int maxsz = 40;
const int B = 35;
int pri[maxn], phi[maxn], mu[maxn];
LL inv[maxn];
bool v[maxn];
LL f[maxn];
std::vector<LL> g[maxn];
std::vector<LL> s[maxsz][maxsz];
inline LL qpow(LL x, LL k) {
LL s = 1;
for (; k; x = x * x % mod, k >>= 1)
if (k & 1) s = s * x % mod;
return s;
}
inline void sieve(int n) {
phi[1] = mu[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!v[i]) { phi[i] = i - 1; mu[i] = -1; pri[++pri[0]] = i; }
for (int j = 1; j <= pri[0]; ++j) {
int x = pri[j] * i;
if (x > n) break;
v[x] = 1;
if (!(i % pri[j])) {
phi[x] = pri[j] * phi[i];
mu[x] = 0;
break;
} else {
phi[x] = phi[pri[j]] * phi[i];
mu[x] = -mu[i];
}
}
}
}
inline void init(int n) {
inv[1] = 1;
for (int i = 2; i <= n; ++i)
inv[i] = (mod - mod / i * inv[mod % i]) % mod;
for (int d = 1; d <= n; ++d)
for (int T = d; T <= n; T += d)
f[T] += d * inv[phi[d]] % mod * mu[T / d];
for (int j = 1; j <= n; ++j) {
g[j].push_back(0);
for (int i = 1; i <= n / j; ++i) {
LL val = (g[j][i - 1] + phi[i * j]) % mod;
g[j].push_back(val);
}
}
for (int x = 1; x <= B; ++x)
for (int y = 1; y <= B; ++y) {
int len = n / std::max(x, y);
s[x][y].push_back(0);
for (int i = 1; i <= len; ++i) {
LL val = (s[x][y][i - 1] + f[i] * g[i][x] % mod * g[i][y] % mod + mod) % mod;
s[x][y].push_back(val);
}
}
}
inline LL solve(int n, int m) {
if (n > m) std::swap(n, m);
LL ans = 0;
for (int i = 1; i <= m / B; ++i)
ans = (ans + f[i] * g[i][n / i] % mod * g[i][m / i] % mod + mod) % mod;
for (int l = m / B + 1, r; l <= n; l = r + 1) {
r = std::min(n / (n / l), m / (m / l));
ans = (ans + (s[n / l][m / l][r] - s[n / l][m / l][l - 1]) % mod + mod % mod) % mod;
}
return (ans % mod + mod) % mod;
}
int main() {
int T; read(T);
sieve(1e5); init(1e5);
while (T--) {
int n, m; read(n, m);
writeln(solve(n, m));
}
return 0;
}