洛谷传送门
BZOJ传送门
题目描述
有一张 N ∗ m N*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的数之和。
输入输出格式
输入格式:
输入包含多组数据。
输入的第一行一个整数 Q Q Q表示测试点内的数据组数
接下来 Q Q Q行,每行三个整数 n n n, m m m, a ( ∣ a ∣ ≤ 1 0 9 ) a(|a| \le 10^9) a(∣a∣≤109)描述一组数据。
输出格式:
对每组数据,输出一行一个整数,表示答案模 2 31 2^{31} 231的值。
输入输出样例
输入样例#1:
2
4 4 3
10 10 5
输出样例#1:
20
148
说明
1 ≤ N . m ≤ 1 0 5 1 \le N.m \le 10^5 1≤N.m≤105 , 1 ≤ Q ≤ 2 ∗ 1 0 4 1 \le Q \le 2*10^4 1≤Q≤2∗104
解题分析
还是先大力推式子:
∑
i
=
1
n
∑
j
=
1
m
σ
1
(
g
c
d
(
i
,
j
)
)
[
σ
1
(
g
c
d
(
i
,
j
)
)
<
a
]
=
∑
d
=
1
n
σ
1
(
d
)
[
σ
1
(
d
)
<
a
]
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
d
]
=
∑
d
=
1
n
σ
1
(
d
)
[
σ
1
(
d
)
<
a
]
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
∑
p
∣
g
c
d
(
i
,
j
)
μ
(
p
)
=
∑
d
=
1
n
σ
1
(
d
)
[
σ
1
(
d
)
<
a
]
∑
p
=
1
⌊
n
d
⌋
μ
(
p
)
⌊
n
d
p
⌋
⌊
m
d
p
⌋
\sum_{i=1}^{n}\sum_{j=1}^{m}\sigma_1(gcd(i,j))[\sigma_1(gcd(i,j))<a] \\ =\sum_{d=1}^n\sigma_1(d)[\sigma_1(d)<a]\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=d] \\ =\sum_{d=1}^n\sigma_1(d)[\sigma_1(d)<a]\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{p|gcd(i,j)}\mu(p) \\ =\sum_{d=1}^n\sigma_1(d)[\sigma_1(d)<a]\sum_{p=1}^{\lfloor\frac{n}{d}\rfloor}\mu(p)\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor
i=1∑nj=1∑mσ1(gcd(i,j))[σ1(gcd(i,j))<a]=d=1∑nσ1(d)[σ1(d)<a]i=1∑nj=1∑m[gcd(i,j)=d]=d=1∑nσ1(d)[σ1(d)<a]i=1∑⌊dn⌋j=1∑⌊dm⌋p∣gcd(i,j)∑μ(p)=d=1∑nσ1(d)[σ1(d)<a]p=1∑⌊dn⌋μ(p)⌊dpn⌋⌊dpm⌋
套路地设
T
=
d
p
T=dp
T=dp, 那么:
∑
d
=
1
n
σ
1
(
d
)
[
σ
1
(
d
)
<
a
]
∑
p
=
1
⌊
n
d
⌋
μ
(
p
)
⌊
n
d
p
⌋
⌊
m
d
p
⌋
=
∑
T
=
1
n
⌊
n
T
⌋
⌊
m
T
⌋
∑
d
∣
T
σ
1
(
d
)
[
σ
1
(
d
)
<
a
]
μ
(
T
d
)
\sum_{d=1}^n\sigma_1(d)[\sigma_1(d)<a]\sum_{p=1}^{\lfloor\frac{n}{d}\rfloor}\mu(p)\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor \\ =\sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{d|T}\sigma_1(d)[\sigma_1(d)<a]\mu(\frac{T}{d})
d=1∑nσ1(d)[σ1(d)<a]p=1∑⌊dn⌋μ(p)⌊dpn⌋⌊dpm⌋=T=1∑n⌊Tn⌋⌊Tm⌋d∣T∑σ1(d)[σ1(d)<a]μ(dT)
然后我们发现似乎这个玩意资瓷我们更改
a
a
a的操作? 设
G
(
T
)
=
∑
d
∣
T
σ
1
(
d
)
[
σ
1
(
d
)
<
a
]
μ
(
T
d
)
G(T)=\sum_{d|T}\sigma_1(d)[\sigma_1(d)<a]\mu(\frac{T}{d})
G(T)=∑d∣Tσ1(d)[σ1(d)<a]μ(dT),只要我们离线询问, 然后按
a
a
a排序,并把
σ
1
(
i
)
\sigma_1(i)
σ1(i)也排个序, 每次更新可用的
G
(
T
)
G(T)
G(T)的值, 然后用
B
I
T
BIT
BIT维护区间和, 套上下底分块即可。
代码如下:
#include <cstdio>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <climits>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
#define ll long long
#define lbt(i) ((i) & (-(i)))
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int pcnt, T;
int miu[MX], pri[MX], sig1[MX];
bool npr[MX];
ll G[MX], ans[MX];
const unsigned int MOD = (1u << 31);
struct INFO {int n, m, lim, id;} que[MX];
struct DAT {int pos, val;} dat[MX];
IN bool operator < (const INFO &x, const INFO &y) {return x.lim < y.lim;}
IN bool operator < (const DAT &x, const DAT &y) {return x.val < y.val;}
IN void add(R int pos, R int val) {for (; pos <= 1e5; pos += lbt(pos)) (G[pos] += val) %= MOD;}
IN ll query(R int pos)
{
ll ret = 0;
for (; pos; pos -= lbt(pos)) ret += G[pos];
return ret;
}
IN void modify(R int pos, R int val)
{
for (R int i = 1; i * pos <= 1e5; i++)
add(pos * i, val * miu[i]);
}
IN ll getit(R int lef, R int rig) {return query(rig) - query(lef - 1);}
void get()
{
miu[1] = 1; int tar;
for (R int i = 2; i <= 1e5; ++i)
{
if (!npr[i]) pri[++pcnt] = i, miu[i] = -1;
for (R int j = 1; j <= pcnt; ++j)
{
tar = i * pri[j];
if (tar > 1e5) break;
npr[tar] = true;
if (!(i % pri[j])) {miu[tar] = 0; break;}
miu[tar] = -miu[i];
}
}
for (R int i = 1; i <= 1e5; ++i)
for (R int j = i; j <= 1e5; j += i)
sig1[j] += i;
for (R int i = 1; i <= 1e5; ++i) dat[i] = {i, sig1[i]};
}
signed main(void)
{
in(T); get();
for (R int i = 1; i <= T; ++i)
in(que[i].n), in(que[i].m), in(que[i].lim), que[i].id = i;
std::sort(dat + 1, dat + 1 + 100000);
std::sort(que + 1, que + 1 + T);
for (R int i = 1, cur = 1; i <= T; ++i)
{
W (cur <= 1e5 && dat[cur].val <= que[i].lim) modify(dat[cur].pos, dat[cur].val), ++cur;
if (que[i].n > que[i].m) std::swap(que[i].n, que[i].m);
for (R int lef = 1, rig; lef <= que[i].n; lef = rig + 1)
{
rig = std::min(que[i].n / (que[i].n / lef), que[i].m / (que[i].m / lef));
ans[que[i].id] += 1ll * (que[i].n / lef) * (que[i].m / lef) * getit(lef, rig);
}
}
for (R int i = 1; i <= T; ++i) printf("%lld\n", (ans[i] % MOD + MOD) % MOD);
}