(markdown下题面排版好像很丑。。还是贴链接吧
很显然的莫比乌斯
很容易就能写出式子:
f(d)=∑d|nn<=min(a,b) F(n)μ(nd)
f
(
d
)
=
∑
n
<=
m
i
n
(
a
,
b
)
d
|
n
F
(
n
)
μ
(
n
d
)
其中
F(n)=⌊an⌋⌊bn⌋
F
(
n
)
=
⌊
a
n
⌋
⌊
b
n
⌋
但是这个式子显然不可优化。。那么考虑换一种形式
f(d)=∑⌊min(a,b)d⌋D=1F(Dd)μ(D) f ( d ) = ∑ D = 1 ⌊ m i n ( a , b ) d ⌋ F ( D d ) μ ( D )
然后考虑 F(n) F ( n ) 这个式子里有两个向下取整。。。联想一下因数个数就会知道可能会有大量连续的 D D 使得相等
这些相等的值显然可以一起算,可以把 F(Dd) F ( D d ) 从和式中取出来,然后后面的 μ μ 用一个前缀和维护起来快速计算
然后就做完了==
代码:
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<algorithm>
#include<cstdlib>
#include<stack>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int INF = 2147483647;
const int maxn = 50010;
LL crz;
int a,b,d;
int p[maxn],tot,mu[maxn],sum[maxn];
bool mark[maxn];
inline LL getint()
{
LL ret = 0,f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
ret = ret * 10 + c - '0',c = getchar();
return ret * f;
}
inline void init(int n)
{
mu[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!mark[i]) p[++tot] = i , mu[i] = -1;
for (int j = 1; j <= tot; j++)
{
if (i * p[j] > n) break;
mark[p[j] * i] = 1;
if (i % p[j]) mu[p[j] * i] = -mu[i];
else
{
mu[p[j] * i] = 0;
break;
}
}
}
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + mu[i];
}
inline LL cal(int a,int b,int D)
{
int c = min(a / d,b / d);
int na = a / D,nb = b / D;
LL ret = 0;
for (int d = 1; d != c;)
{
int nex = min(na / (na / (d + 1)),nb / (nb / (d + 1)));
ret += 1ll * (na / nex) * (nb / nex) * (sum[nex] - sum[d]);
d = nex;
}
ret += 1ll * mu[1] * (na / 1) * (nb / 1);
return ret;
}
int main()
{
#ifdef AMC
freopen("AMC1.txt","r",stdin);
#endif
int n = getint();
init(50000);
for (int i = 1; i <= n; i++)
{
a = getint(); b = getint(); d = getint();
printf("%lld\n",cal(a,b,d));
}
return 0;
}