Description
有一张 n×m 的数表,其第 i 行第 j 列(1 <= i <= n, 1 <= j <= m)的数值为
能同时整除 i 和 j 的所有自然数之和。给定 a , 计算数表中不大于 a 的数之和。
Input
输入包含多组数据。
输入的第一行一个整数Q表示测试点内的数据组数
接下来Q行,每行三个整数n,m,a(|a| < =10^9)描述一组数据。
1 < =N.m < =10^5 , 1 < =Q < =2×10^4
Output
对每组数据,输出一行一个整数,表示答案模2^31的值。
Sample Input
2
4 4 3
10 10 5
Sample Output
20
148
HINT
令
F[x]
F
[
x
]
表示
x
x
的因数之和
然后某一格的数字为
不考虑
a
a
的限制
=∑min(n,m)d=gcdF[d]∑min(⌊nd⌋,⌊md⌋)t=1μ(t)∗⌊ntd⌋∗⌊mtd⌋
=
∑
d
=
g
c
d
m
i
n
(
n
,
m
)
F
[
d
]
∑
t
=
1
m
i
n
(
⌊
n
d
⌋
,
⌊
m
d
⌋
)
μ
(
t
)
∗
⌊
n
t
d
⌋
∗
⌊
m
t
d
⌋
老套路 令
T=td
T
=
t
d
=∑min(n,m)T=1⌊nT⌋⌊mT⌋∑d|TF(d)∗μ(Td)
=
∑
T
=
1
m
i
n
(
n
,
m
)
⌊
n
T
⌋
⌊
m
T
⌋
∑
d
|
T
F
(
d
)
∗
μ
(
T
d
)
=∑min(n,m)T=1⌊nT⌋⌊mT⌋g(T)
=
∑
T
=
1
m
i
n
(
n
,
m
)
⌊
n
T
⌋
⌊
m
T
⌋
g
(
T
)
求出
g(T)
g
(
T
)
的前缀和就可以做出来了
那么对于没有
a
a
的限制是可以轻松AC的
那如果有呢
我们考虑离线做。
把输入按照
a
a
升序
把数组按照
F(i)
F
(
i
)
升序
按照顺序加入树状数组维护即可
对
int
i
n
t
取模我们可以直接满溢
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 101000;
int prime[maxn] , miu[maxn];
bool flag[maxn];
int Q , tr[maxn];
int ans[maxn];
int mx;
const int MOD = 1<<31;
struct data
{
int n , m , a , id;
}q[20100];
pair<int,int> F[maxn];
int read()
{
int sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(flag) return sum;
else return -sum;
}
void add(int x,int val)
{
for(int i = x;i <= mx;i += i & (-i)) tr[i] += val;
}
int find(int x)
{
int sum = 0;
for(int i = x;i;i -= i&(-i)) sum += tr[i];
return sum;
}
bool mycmp(data a,data b)
{
return a.a < b.a;
}
void first()
{
Q = read();
for(int i = 1;i <= Q;++i)
q[i].n = read() , q[i].m = read() , q[i].a = read() , q[i].id = i,
mx = max(mx , max(q[i].n , q[i].m));
sort(q + 1,q + Q + 1,mycmp);
miu[1] = 1;
for(int i = 2;i <= mx;++i)
{
if(!flag[i])
prime[++prime[0]] = i , miu[i] = -1;
for(int j = 1;j <= prime[0] && i * prime[j] <= mx;++j)
{
flag[i * prime[j]] = true;
if(i % prime[j] == 0) break;
miu[i * prime[j]] = -miu[i];
}
}
for(int i = 1;i <= mx;++i)
for(int j = i;j <= mx;j += i)
F[j].first += i;
for(int i = 1;i <= mx;++i) F[i].second = i;
sort(F + 1,F + mx + 1);
return;
}
void solve(int x)
{
int n = q[x].n , m = q[x].m , id = q[x].id;
int T = 1;
if( n > m ) swap( n , m );
while(T <= n)
{
int i = min( n / ( n / T ) , m / ( m / T ));
ans[id] += 1ll * ( n / T ) * ( m / T ) * (find(i) - find(T - 1)) ;
T = i + 1;
}
return;
}
int main()
{
first();
int now = 1;
for(int i = 1;i <= Q;++i)
{
while(now <= mx && F[now].first <= q[i].a)
{
for(int j = F[now].second;j <= mx;j += F[now].second)
add( j , F[now].first * miu[j / F[now].second]);
now++;
}
solve(i);
}
for(int i = 1;i <= Q;++i)
printf("%d\n",ans[i]&0x7fffffff);
return 0;