题面:**
太长了,懒得粘了
懵逼乌斯反演入门题。
首先,我们要求的是这个柿子:
(displaystyle sum_{i=1}^{n}sum_{j=1}^{m} [gcd(i,j) == k]) (这里的 (n,m) 即题面中的 (a,b) 。 (k) 为题面中的 (d))
为了省事(偷懒) 我们在这里默认 (n leq m)
按套路,提取一个 (k) 出来变成:
(displaystylesum_{i=1}^{nover k} sum_{j=1}^{mover k} [gcd(i,j) == 1])
由莫比乌斯反演可以得到一个等式:
(displaystyle [gcd(i,j) == 1] = sum_{d=1,dmid i,dmid j}^{n} mu(d))
把这个柿子回代到原来的柿子可得:
(displaystylesum_{i=1}^{nover k} sum_{j=1}^{mover k}sum_{dmid i, dmid k} mu(d))
先枚举一下 (d) 变成:
(displaystylesum_{d=1}^{nover k} mu(d) sum_{i=1}^{nover k} [dmid i] sum_{j = 1}^{mover k}[dmid k])
在变换一下可以变成:
(displaystylesum_{d=1}^{nover k} mu(d) lfloor{nover kd} floorlfloor{mover kd} floor)
这就是我们最后的总柿子。
在对 (mu) 函数值求个前缀和,在套个数论分块即可。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
const int N = 5e4+10;
int t,n,m,d,tot;
int prime[N],mu[N],sum[N];
bool check[N];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
return s * w;
}
void YYCH()
{
mu[1] = 1;
for(int i = 2; i <= N-5; i++)//筛 mu 函数
{
if(!check[i])
{
prime[++tot] = i;
mu[i] = -1;
}
for(int j = 1; j <= tot && i * prime[j] <= N-5; j++)
{
check[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
for(int i = 1; i <= N-5; i++)//前缀和
{
sum[i] = sum[i-1] + mu[i];
}
}
LL slove(int n,int m,int d)
{
LL res = 0;
for(int l = 1,r; l <= n/d; l = r+1)//数论分块
{
r = min(n/(n/l),m/(m/l));
res += 1LL * (sum[r] - sum[l-1]) * (n/l/d) * (m/l/d);
}
return res;
}
int main()
{
t = read(); YYCH();
while(t--)
{
n = read(); m = read(); d = read();
if(n > m) swap(n,m);
printf("%lld
",slove(n,m,d));
}
return 0;
}