[ SDOI 2017 ] 数字表格
题目
思路
代码
#include <iostream>
#include <cstring>
#define int long long
using namespace std;
const int N = 1000010, mod = 1e9 + 7;
int T, n, m, f[N], invf[N], G[N], S[N];
int P[N], M[N], V[N], cnt;
int qmi(int n, long long k) {
int res = 1 % mod;
for (; k; k >>= 1, n = 1ll * n * n % mod)
if (k & 1) res = 1ll * res * n % mod;
return res;
}
void init(int n) {
// M: 莫比乌斯函数, f: 斐波那契, invf: 斐波那契逆元, S: 前缀积
M[1] = f[1] = invf[1] = S[0] = 1;
for (int i = 2; i <= n; i++) {
f[i] = (f[i - 1] + f[i - 2]) % mod;
invf[i] = qmi(f[i], mod - 2);
if (!V[i]) P[++cnt] = i, M[i] = -1;
for (int j = 1; P[j] <= n / i; j++) {
V[P[j] * i] = true;
if (i % P[j] == 0) { M[P[j] * i] = 0; break; }
M[i * P[j]] = -M[i];
}
}
// G函数, 求积先都初始化成 1
for (int i = 1; i <= n; i++) G[i] = 1;
for (int d = 1; d <= n; d++) {
// M[d] = 0 的时候答案就是 1
if (M[d] != 0)
// T 是 d 的倍数
for (int T = d; T <= n; T += d)
// 判断一下 M[d] 是不是 -1, 如果是的话就乘 f 的逆元
G[T] = 1ll * G[T] * (M[d] == 1 ? f[T / d] : invf[T / d]) % mod;
S[d] = 1ll * S[d - 1] * G[d] % mod;
}
}
// 这个就是返回当前块内, n / k 值不变的最大的 k
int g(int n, int k) { return n / (n / k); }
signed main() {
init(N - 1);
cin >> T;
while (T-- && cin >> n >> m) {
int res = 1;
// 处理一下 n, m
if (n > m) swap(n, m);
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n, min(g(n, l), g(m, l)));
// 这里就是 g(T)
// 由于是乘法, 前缀积是 S[r] / S[l - 1], 求逆元即可
int t = 1ll * S[r] * qmi(S[l - 1], mod - 2) % mod;
// 这里的 (n / l) * (m / l) 可能会爆 int
res = 1ll * res * qmi(t, (n / l) * (m / l)) % mod;
}
cout << res << endl;
}
return 0;
}