[ SDOI 2017 ] 数字表格

题目

luogu
LOJ
Acwing

思路

1.png
2.png
3.png

代码

#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;
}
posted @ 2021-04-02 17:06  Protein_lzl  阅读(49)  评论(0编辑  收藏  举报