【题目链接】
【前置技能】
- 线性筛
- 反演
【题解】
-
∑
i
=
1
n
∑
j
=
1
m
g
c
d
(
i
,
j
)
k
\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m} {gcd(i,j)^k}
i=1∑nj=1∑mgcd(i,j)k
= ∑ x x k ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = x ] =\displaystyle\sum_x {x^k}\sum_{i=1}^{n} \sum_{j=1}^{m} {[gcd(i,j)=x]} =x∑xki=1∑nj=1∑m[gcd(i,j)=x]
= ∑ x x k ∑ i = 1 ⌊ n x ⌋ ∑ j = 1 ⌊ m x ⌋ ϵ ( g c d ( i , j ) ) =\displaystyle\sum_x {x^k}\sum_{i=1}^{\lfloor\frac n x\rfloor} \sum_{j=1}^{\lfloor\frac m x\rfloor} {\epsilon(gcd(i,j))} =x∑xki=1∑⌊xn⌋j=1∑⌊xm⌋ϵ(gcd(i,j))
= ∑ x x k ∑ i = 1 ⌊ n x ⌋ ∑ j = 1 ⌊ m x ⌋ ∑ d ∣ g c d ( i , j ) μ ( d ) =\displaystyle\sum_x {x^k}\sum_{i=1}^{\lfloor\frac n x\rfloor} \sum_{j=1}^{\lfloor\frac m x\rfloor} \sum_{d|gcd(i,j)} {\mu (d)} =x∑xki=1∑⌊xn⌋j=1∑⌊xm⌋d∣gcd(i,j)∑μ(d)
= ∑ x x k ∑ d μ ( d ) ∑ i = 1 ⌊ n x ⌋ [ d ∣ i ] ∑ j = 1 ⌊ m x ⌋ [ d ∣ j ] =\displaystyle\sum_x {x^k} \sum_{d} {\mu (d)} \sum_{i=1}^{\lfloor\frac n x\rfloor} {[d|i]} \sum_{j=1}^{\lfloor\frac m x\rfloor}{[d|j]} =x∑xkd∑μ(d)i=1∑⌊xn⌋[d∣i]j=1∑⌊xm⌋[d∣j]
= ∑ x x k ∑ d μ ( d ) ⌊ n x d ⌋ ⌊ m x d ⌋ =\displaystyle\sum_x {x^k} \sum_{d} {\mu (d)} \lfloor\frac n {xd}\rfloor \lfloor\frac m {xd}\rfloor =x∑xkd∑μ(d)⌊xdn⌋⌊xdm⌋
$=\displaystyle \sum i \lfloor\frac n i \rfloor \lfloor\frac m i \rfloor \sum{d|i} {d^k * \mu (\frac{i}{d})} $ - 令 F ( i ) = ∑ d ∣ i d k ∗ μ ( i d ) \displaystyle F(i)= \sum_{d|i} {d^k * \mu (\frac{i}{d})} F(i)=d∣i∑dk∗μ(di),原式 = ∑ i ⌊ n i ⌋ ⌊ m i ⌋ F ( i ) =\displaystyle \sum _i \lfloor\frac n i \rfloor \lfloor\frac m i \rfloor F(i) =i∑⌊in⌋⌊im⌋F(i)
- 发现 F F F是两个积性函数的卷积,也是一个积性函数,可以线性筛预处理出 F F F的值。然后按照 ⌊ n i ⌋ ⌊ m i ⌋ \lfloor\frac n i \rfloor \lfloor\frac m i \rfloor ⌊in⌋⌊im⌋的值分成 O ( n ) O(\sqrt n) O(n)组计算。
- 时间复杂度 O ( N + Q N ) O(N+Q\sqrt N) O(N+QN)(NM同阶)
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 5000010
#define mod 1000000007
using namespace std;
int Q, n, m, k, p[MAXN], pri[350000], cnt;
LL f[MAXN], q[MAXN];
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
LL qpow(LL a, int b){
LL ret = 1;
while (b){
if (b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
void init(){
for (int i = 2; i <= 5000000; ++i){
if (!p[i]) p[i] = i, pri[++cnt] = i;
for (int j = 1; j <= cnt && pri[j] <= p[i] && pri[j] * i <= 5000000; ++j)
p[pri[j] * i] = pri[j];
}
f[1] = 1;
for (int i = 2; i <= 5000000; ++i){
if (p[i / p[i]] == p[i]) q[i] = q[i / p[i]] * p[i];
else q[i] = p[i];
if (p[i] == i) f[i] = (mod + qpow(i, k) - 1) % mod;
else if (q[i] == i) f[i] = f[i / p[i]] * qpow(p[i], k) % mod;
else f[i] = f[q[i]] * f[i / q[i]] % mod;
}
for (int i = 2; i <= 5000000; ++i)
f[i] = (f[i] + f[i - 1]) % mod;
}
LL work(){
LL ret = 0;
for (int i = 1; i <= n && i <= m; ++i){
int nxt = n / (n / i), mxt = m / (m / i);
if (nxt <= mxt){
ret += 1ll * (n / i) * (m / i) % mod * (f[nxt] - f[i - 1]) % mod;
ret %= mod;
i = nxt;
} else {
ret += 1ll * (n / i) * (m / i) % mod * (f[mxt] - f[i - 1]) % mod;
ret %= mod;
i = mxt;
}
}
if (ret < 0) ret += mod;
return ret;
}
int main(){
read(Q), read(k);
init();
while (Q--){
read(n), read(m);
LL ans = work();
printf("%lld\n", ans);
}
return 0;
}