P3704 [SDOI2017]数字表格 题解
\[\prod_{i=1}^{n} \prod_{j=1}^{m} f_{\gcd(i,j)}\\
=\prod_{d=1}^{n}f_d^{\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)==d]}\\
\]
\[\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==d]\\
=\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}} [gcd(i,j)==1]\\
=\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}\sum_{x|gcd(i,j)}\mu(x)\\
=\sum_{x=1}^{\frac{n}{d}}\mu(x)\dfrac{n}{dx}\dfrac{m}{dx}
\]
\[=\prod_{d=1}^{n}f_d^{\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)==d]}\\
=\prod_{d=1}^{n}f_d^{\sum\limits_{x=1}^{\frac{n}{d}}\mu(x)\frac{n}{dx}\frac{m}{dx}}\\
=\prod_{d=1}^{n}f_d^{\sum\limits_{T=1,d|T}^{n}\mu(\frac{T}{d})\frac{n}{T}\frac{m}{T}}\\
=\prod_{T=1}^{n}\prod_{d|T}^{n}f_d^{\mu(\frac{T}{d})\frac{n}{T}\frac{m}{T}}\\
=\prod_{T=1}^{n}\left(\prod_{d|T}^{n}f_d^{\mu(\frac{T}{d})}\right)^{\frac{n}{T}\frac{m}{T}}\\
\]
\(O(n\log n)\) 预处理出 \(f_i\) 的逆元就可以 \(O(n\ln n)\) 筛出 \(g(T)=\prod\limits_{d|T}^{n}f_d^{\mu(\frac{T}{d})}\) 了。
询问的时候整除分块加个快速幂即可,时间复杂度 \(O(n\log mod+n\ln n+q\sqrt{n}\log mod)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return x*f;
}
const int mod = 1e9 + 7;
int T, n, m;
const int N = 1e6 + 5;
int ivb[N], mu[N], fib[N], pri[N], cnt, f[N];
bool vis[N];
int qpow(int n, int k, int res = 1) {
for(;k; k >>= 1, n = 1ll * n * n % mod)
if (k & 1)res = 1ll * res * n % mod;
return res;
}
int power(int n, int k){
if (k == -1) return ivb[n];
if (k == 0) return 1;
if (k == 1) return fib[n];
}
void init(const int&N) {
fib[1] = 1, mu[1] = 1, ivb[1] = 1;
for (int i = 2; i <= N; ++ i) fib[i] = (fib[i - 1] + fib[i - 2]) % mod, ivb[i] = qpow(fib[i], mod - 2);
for (int i = 2; i <= N; ++ i) {
if(!vis[i]) pri[++ cnt] = i, mu[i] = -1;
for (int j = 1; j <= cnt && i * pri[j] <= N; ++ j) {
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
mu[i * pri[j]] = - mu[i];
}
}
for (int i = 0; i <= N; ++ i) f[i] = 1;
for (int i = 1; i <= N; ++ i)
for (int j = 1; i * j <= N; ++ j)
f[i * j] = 1ll * f[i * j] * power(i, mu[j]) % mod;
for (int i = 1; i <= N; ++ i) f[i] = 1ll * f[i] * f[i - 1] % mod;
}
int query(int n, int m){
if(n > m) n ^= m ^= n ^= m;
int res = 1;
for(int l = 1, r;l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
res = 1ll * res * qpow(1ll * f[r] * qpow(f[l - 1], mod - 2) % mod, 1ll * (n / l) * (m / l) % (mod - 1)) % mod;
}
return res;
}
signed main() {
init(N - 5);
T = read();
while(T --) {
n = read(), m = read();
printf("%d\n",query(n,m));
}
}
路漫漫其修远兮,吾将上下而求索