题目让你求
∏
i
=
1
n
∏
j
=
1
m
f
[
g
c
d
(
i
,
j
)
]
\prod_{i = 1}^n\prod_{j = 1}^m f[gcd(i,j)]
∏i=1n∏j=1mf[gcd(i,j)]
化简一下式子,枚举一下 gcd:
∏
d
=
1
n
∏
i
=
1
n
∏
j
=
1
m
f
[
d
]
∗
[
g
c
d
(
i
,
j
)
=
d
]
=
∏
d
=
1
n
f
(
d
)
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
d
]
\prod_{d = 1}^n\prod_{i = 1}^n\prod_{j = 1}^m f[d] * [gcd(i,j)=d] = \prod_{d = 1}^nf(d)^{\sum_{i = 1}^n\sum_{j = 1}^m[gcd(i,j) = d]}
d=1∏ni=1∏nj=1∏mf[d]∗[gcd(i,j)=d]=d=1∏nf(d)∑i=1n∑j=1m[gcd(i,j)=d]观察这个式子,
f
(
d
)
f(d)
f(d)的指数部分可以拿出来反演化简了:
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
d
]
=
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
g
c
d
(
i
,
j
)
=
1
]
\sum_{i = 1}^n\sum_{j = 1}^m[gcd(i,j) = d] = \sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j = 1}^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j) = 1]
i=1∑nj=1∑m[gcd(i,j)=d]=i=1∑⌊dn⌋j=1∑⌊dm⌋[gcd(i,j)=1]
令
f
(
p
)
=
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
g
c
d
(
i
,
j
)
=
p
]
令f(p) = \sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j = 1}^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j) = p]
令f(p)=i=1∑⌊dn⌋j=1∑⌊dm⌋[gcd(i,j)=p]
F
(
x
)
=
∑
x
∣
p
f
[
p
]
=
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
x
∣
g
c
d
(
i
,
j
)
]
F(x) = \sum_{x | p}f[p] = \sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j = 1}^{\lfloor\frac{m}{d}\rfloor}[x|gcd(i,j)]
F(x)=x∣p∑f[p]=i=1∑⌊dn⌋j=1∑⌊dm⌋[x∣gcd(i,j)]
反
演
一
下
:
f
(
p
)
=
∑
p
∣
x
μ
(
x
p
)
∗
F
(
x
)
,
f
(
1
)
=
∑
x
=
1
⌊
n
d
⌋
μ
(
x
)
∗
F
(
x
)
=
∑
x
=
1
⌊
n
d
⌋
μ
(
x
)
∗
⌊
n
x
∗
d
⌋
∗
⌊
m
x
∗
d
⌋
反演一下:f(p) = \sum_{p | x}\mu(\frac{x}{p}) *F(x),f(1) = \sum_{x = 1}^{\lfloor\frac{n}{d}\rfloor}\mu(x) *F(x) = \sum_{x = 1}^{\lfloor\frac{n}{d}\rfloor}\mu(x)*{\lfloor\frac{n}{x * d}\rfloor}*{\lfloor\frac{m}{x * d}\rfloor}
反演一下:f(p)=p∣x∑μ(px)∗F(x),f(1)=x=1∑⌊dn⌋μ(x)∗F(x)=x=1∑⌊dn⌋μ(x)∗⌊x∗dn⌋∗⌊x∗dm⌋
化简到这个程度可以
O
(
n
n
)
O(n\sqrt n)
O(nn)预处理指数部分,然后每次询问分块,用欧拉降幂加快速幂在
O
(
T
∗
n
∗
l
o
g
(
m
o
d
)
)
O(T * \sqrt n * log(mod))
O(T∗n∗log(mod))时间内完成单组询问,但
n
≤
1
0
6
,
O
(
n
∗
n
)
n \leq 10 ^6 ,O(n * \sqrt n)
n≤106,O(n∗n)不够通过此题,继续化简,将指数部分枚举 x 的地方拿下来:
∏
d
=
1
n
f
(
d
)
∑
x
=
1
⌊
n
d
⌋
μ
(
x
)
∗
⌊
n
x
∗
d
⌋
∗
⌊
m
x
∗
d
⌋
=
∏
d
=
1
n
∏
x
=
1
⌊
n
d
⌋
f
(
d
)
m
u
(
x
)
∗
⌊
n
x
∗
d
⌋
∗
⌊
m
x
∗
d
⌋
\prod_{d = 1}^nf(d)^{\sum_{x = 1}^{\lfloor\frac{n}{d}\rfloor}\mu(x)*{\lfloor\frac{n}{x * d}\rfloor}*{\lfloor\frac{m}{x * d}\rfloor}} = \prod_{d = 1}^n\prod_{x = 1}^{\lfloor\frac{n}{d}\rfloor}f(d)^{\\mu(x)*{\lfloor\frac{n}{x * d}\rfloor}*{\lfloor\frac{m}{x * d}\rfloor}}
d=1∏nf(d)∑x=1⌊dn⌋μ(x)∗⌊x∗dn⌋∗⌊x∗dm⌋=d=1∏nx=1∏⌊dn⌋f(d)mu(x)∗⌊x∗dn⌋∗⌊x∗dm⌋
令
T
=
x
∗
d
T = x * d
T=x∗d,改变枚举项:
∏
T
=
1
n
∏
d
∣
T
f
(
d
)
m
u
(
T
d
)
∗
⌊
n
T
⌋
∗
⌊
m
T
⌋
\prod_{T = 1}^n\prod_{d | T}f(d)^{\\mu(\frac{T}{d})*{\lfloor\frac{n}{T}\rfloor}*{\lfloor\frac{m}{T}\rfloor}}
T=1∏nd∣T∏f(d)mu(dT)∗⌊Tn⌋∗⌊Tm⌋
令
S
(
T
)
=
∏
d
∣
T
f
(
d
)
μ
(
T
d
)
S(T) = \prod_{d | T}f(d)^{\mu(\frac{T}{d})}
S(T)=∏d∣Tf(d)μ(dT),可以
O
(
n
∗
l
o
g
n
)
O(n * logn)
O(n∗logn)预处理得到 S函数。
∏
T
=
1
n
S
(
T
)
⌊
n
T
⌋
∗
⌊
m
T
⌋
\prod_{T = 1}^nS(T)^{{\lfloor\frac{n}{T}\rfloor}*{\lfloor\frac{m}{T}\rfloor}}
T=1∏nS(T)⌊Tn⌋∗⌊Tm⌋
预处理一下S函数前缀乘积和逆元,再分下块,每次询问
O
(
n
∗
l
o
g
(
m
o
d
)
)
O(\sqrt n * log(mod))
O(n∗log(mod))解决,总复杂度
O
(
n
∗
l
o
g
n
+
T
∗
n
∗
l
o
g
(
m
o
d
)
)
O(n * logn + T * \sqrt n * log(mod))
O(n∗logn+T∗n∗log(mod))
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 2e6 + 10;
typedef long long ll;
ll fpow(ll a,ll b) {
ll r = 1;
while(b) {
if(b & 1) r = r * a % mod;
a = a * a % mod;
b >>= 1;
}
return r;
}
bool ispri[maxn];
int pri[maxn],mu[maxn];
ll ans[maxn],inv[maxn];
int f[maxn];
int t,n,m;
void sieve(int n) {
ispri[0] = ispri[1] = true;
pri[0] = 0;mu[1] = 1;
for(int i = 2; i <= n; i++) {
if(!ispri[i]) pri[++pri[0]] = i,mu[i] = -1;
for(int j = 1; j <= pri[0] && i * pri[j] <= n; j++) {
ispri[i * pri[j]] = true;
if(i % pri[j] == 0) break;
mu[i * pri[j]] = -mu[i];
}
}
for(int i = 0; i <= n; i++) ans[i] = 1;
f[1] = f[2] = 1;
for(int i = 3; i <= n; i++)
f[i] = (f[i - 1] + f[i - 2]) % mod;
for(int i = 1; i <= n; i++)
inv[i] = fpow(f[i],mod - 2);
for(int i = 1; i <= n; i++) {
for(int j = 1; j * i <= n; j++) {
if(mu[j] == -1)
ans[i * j] = ans[i * j] * inv[i] % mod;
else if(mu[j] == 1)
ans[i * j] = ans[i * j] * f[i] % mod;
}
}
for(int i = 1; i <= n; i++)
ans[i] = ans[i] * ans[i - 1] % mod;
for(int i = 0; i <= n; i++)
inv[i] = fpow(ans[i],mod - 2);
}
int main() {
sieve(1000000);
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&m);
int l,r;
ll res = 1;
if(n > m) swap(n,m);
for(l = 1; l <= n; l = r + 1) {
r = min(n / (n / l),m / (m / l));
ll p1 = n / l,p2 = m / l;
ll tmp = ans[r] * inv[l - 1] % mod;
res = res * fpow(tmp,p1 * p2 % (mod - 1)) % mod;
}
printf("%lld\n",res);
}
return 0;
}