Description
- 多测, Q Q Q 组数据。
- 有一张 n × m n\times m n×m 的数表,其第 i i i 行第 j j j 列( 1 ≤ i ≤ n , 1 ≤ j ≤ m 1\le i\le n, 1\le j\le m 1≤i≤n,1≤j≤m)的数值为能同时整除 i i i 和 j j j 的所有自然数之和。给定 a a a,计算数表中不大于 a a a 的数之和模 2 31 2^{31} 231 的值。
- 1 ≤ n , m ≤ 1 0 5 , 1 ≤ Q ≤ 2 × 1 0 4 , ∣ a ∣ ≤ 1 0 9 1\le n, m\le 10^5, 1\le Q\le 2\times 10^4, |a| \le 10^9 1≤n,m≤105,1≤Q≤2×104,∣a∣≤109。
Solution
首先模 2 31 2^{31} 231 就直接用 int \text{int} int 存自然溢出,最后和 2 31 − 1 2^{31} - 1 231−1 取 & 即可。
因为 k ∣ n ∧ k ∣ m ⇔ k ∣ gcd ( n , m ) k\mid n \land k\mid m \Leftrightarrow k\mid \gcd(n, m) k∣n∧k∣m⇔k∣gcd(n,m),所以第 i i i 行第 j j j 列的数是 σ ( gcd ( i , j ) ) \sigma(\gcd(i, j)) σ(gcd(i,j))。
先不考虑关于
a
a
a 的限制,则所求为
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
σ
(
gcd
(
i
,
j
)
)
=
∑
i
=
1
n
∑
j
=
1
m
∑
d
=
1
n
σ
(
d
)
[
gcd
(
i
,
j
)
=
d
]
=
∑
d
=
1
n
σ
(
d
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
gcd
(
i
,
j
)
=
1
]
=
∑
d
=
1
n
σ
(
d
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
∑
k
∣
gcd
(
i
,
j
)
μ
(
k
)
=
∑
d
=
1
n
σ
(
d
)
∑
k
=
1
⌊
n
d
⌋
μ
(
k
)
⌊
n
d
k
⌋
⌊
m
d
k
⌋
=
∑
T
=
1
n
⌊
n
T
⌋
⌊
m
T
⌋
∑
d
∣
T
σ
(
d
)
μ
(
T
d
)
\begin{aligned} ans & = \sum_{i = 1}^n \sum_{j = 1}^m \sigma(\gcd(i, j)) \\ & = \sum_{i = 1}^n \sum_{j = 1}^m \sum_{d = 1}^n \sigma(d) [\gcd(i, j) = d] \\ & = \sum_{d = 1}^n \sigma(d) \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} [\gcd(i, j) = 1] \\ & = \sum_{d = 1}^n \sigma(d) \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} \sum_{k\mid \gcd(i, j)} \mu(k) \\ & = \sum_{d = 1}^n \sigma(d) \sum_{k = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \mu(k) \left\lfloor\dfrac{n}{dk}\right\rfloor \left\lfloor\dfrac{m}{dk}\right\rfloor \\ & = \sum_{T = 1}^n \left\lfloor\dfrac{n}{T}\right\rfloor \left\lfloor\dfrac{m}{T}\right\rfloor \sum_{d\mid T} \sigma(d) \mu\left(\dfrac{T}{d}\right) \end{aligned}
ans=i=1∑nj=1∑mσ(gcd(i,j))=i=1∑nj=1∑md=1∑nσ(d)[gcd(i,j)=d]=d=1∑nσ(d)i=1∑⌊dn⌋j=1∑⌊dm⌋[gcd(i,j)=1]=d=1∑nσ(d)i=1∑⌊dn⌋j=1∑⌊dm⌋k∣gcd(i,j)∑μ(k)=d=1∑nσ(d)k=1∑⌊dn⌋μ(k)⌊dkn⌋⌊dkm⌋=T=1∑n⌊Tn⌋⌊Tm⌋d∣T∑σ(d)μ(dT)
令
f
(
n
)
=
∑
d
∣
n
σ
(
d
)
μ
(
T
d
)
f(n) = \sum_{d\mid n} \sigma(d) \mu\left(\dfrac{T}{d}\right)
f(n)=d∣n∑σ(d)μ(dT)
如果没有关于
a
a
a 的限制,我们有
f
=
σ
∗
μ
=
Id
f = \sigma * \mu = \operatorname{Id}
f=σ∗μ=Id。
加上限制后,需要满足的是 σ ( d ) ≤ a \sigma(d) \le a σ(d)≤a。
我们运用莫队的思想,将所有的 σ \sigma σ 以及 a a a 从小到大排序,枚举 d d d 的倍数加贡献。
我们需要对 f f f 进行单点修改加贡献,数论分块区间查询,直接用树状数组即可。
预处理要枚举 n ln n n\ln n nlnn 次,还要修改,所以是 O ( n ln n log n ) \Omicron(n\ln n\log n) O(nlnnlogn) 的;
查询有 n \sqrt{n} n 个块,块内要 log n \log n logn 查区间和,所以是 O ( Q n log n ) \Omicron(Q \sqrt{n} \log n) O(Qnlogn) 的。
总时间复杂度为 O ( n log 2 n + Q n log n ) \Omicron(n\log^2 n + Q \sqrt{n} \log n) O(nlog2n+Qnlogn)。
Code
//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <algorithm>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;
const int MAXN = 1e5 + 5;
const int N = 1e5;
typedef int arr[MAXN];
arr p, mu, sigma, num;
bool vis[MAXN];
pair<int, int> f[MAXN];
void pre()
{
mu[1] = sigma[1] = 1;
for (int i = 2; i <= N; i++)
{
if (!vis[i])
{
p[++p[0]] = i;
mu[i] = -1;
sigma[i] = num[i] = 1 + i;
}
for (int j = 1; j <= p[0] && i * p[j] <= N; j++)
{
vis[i * p[j]] = true;
if (i % p[j] == 0)
{
mu[i * p[j]] = 0;
sigma[i * p[j]] = sigma[i] / num[i] * (num[i] * p[j] + 1);
num[i * p[j]] = num[i] * p[j] + 1;
break;
}
mu[i * p[j]] = mu[i] * mu[p[j]];
sigma[i * p[j]] = sigma[i] * sigma[p[j]];
num[i * p[j]] = 1 + p[j];
}
}
for (int i = 1; i <= N; i++)
{
f[i] = {sigma[i], i};
}
sort(f + 1, f + N + 1);
}
arr c;
inline int lowbit(int x)
{
return x & -x;
}
void update(int x, int k)
{
for (int i = x; i <= N; i += lowbit(i))
{
c[i] += k;
}
}
int query(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i))
{
res += c[i];
}
return res;
}
int query_range(int l, int r)
{
return query(r) - query(l - 1);
}
struct Query
{
int id, n, m, a;
bool operator <(const Query &x)const
{
return x.a > a;
}
}q[MAXN];
arr ans;
int main()
{
pre();
int Q;
scanf("%d", &Q);
for (int i = 1; i <= Q; i++)
{
int n, m, a;
scanf("%d%d%d", &n, &m, &a);
q[i] = Query{i, n, m, a};
}
sort(q + 1, q + Q + 1);
for (int t = 1, i = 0; t <= Q; t++)
{
int a = q[t].a, val;
while ((val = f[i + 1].first) <= a)
{
int d = f[++i].second;
for (int k = 1; d * k <= N; k++)
{
update(d * k, val * mu[k]);
}
}
int id = q[t].id, n = q[t].n, m = q[t].m;
if (n > m)
{
swap(n, m);
}
for (int l = 1, r; l <= n; l = r + 1)
{
int k1 = n / l, k2 = m / l;
r = min(n / k1, m / k2);
ans[id] += k1 * k2 * query_range(l, r);
}
}
for (int i = 1; i <= Q; i++)
{
printf("%d\n", ans[i] & 2147483647);
}
return 0;
}