算是一道真正意义上的莫比乌斯反演题,模板
第一次用markdown啊,丑的一批
题目要求
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
=
d
]
\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)==d]
i=1∑nj=1∑m[gcd(i,j)==d]
真的和莫比乌斯反演板子一模一样
那么,套路来了
提一个d
∑
i
=
1
n
/
d
∑
j
=
1
m
/
d
[
g
c
d
(
i
,
j
)
=
=
1
]
\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}[gcd(i,j)==1]
i=1∑n/dj=1∑m/d[gcd(i,j)==1]
把u函数弄进去
∑
i
=
1
n
/
d
∑
j
=
1
m
/
d
∑
k
∣
g
c
d
(
i
,
j
)
μ
(
k
)
\sum_{i=1}^{n/d} \sum_{j=1}^{m/d}\sum_{k|gcd(i,j)}μ(k)
i=1∑n/dj=1∑m/dk∣gcd(i,j)∑μ(k)
化简
∑
i
=
1
m
i
n
(
n
/
d
,
m
/
d
)
μ
(
i
)
∗
⌊
n
i
d
⌋
∗
⌊
m
i
d
⌋
\sum_{i=1}^{min(n/d,m/d)} μ(i) * ⌊\frac{n}{id}⌋* ⌊\frac{m}{id}⌋
i=1∑min(n/d,m/d)μ(i)∗⌊idn⌋∗⌊idm⌋
很明显的数论分块
处理一下前缀和就行了,话说下取整符号怎么打啊只能复制
贴代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 50007;
const int maxm = 50000;
ll sum[maxn],tot,prime[maxn],u[maxn];
int vis[maxn];
void fir()
{
u[1]=1;
for(int i=2;i<=maxm;i++)
{
if(!vis[i])
{
prime[++tot]=i;
u[i]=-1;
}
for(int j=1;j<=tot&&i*prime[j]<=maxm;j++)
{
vis[prime[j]*i]=1;
if(i%prime[j]==0)break; // 这句一定放在下一句前面,因为当if成立是,u[prime[j]*i] 为 0
u[prime[j]*i]=-u[i];
}
}
for(int i=1;i<=maxm;i++)sum[i]=sum[i-1]+u[i]; // 维护u的前缀和
}
ll sqr(ll a,ll b,ll d)
{
ll ans=0;
a/=d,b/=d; // ab都为重定义后的ab
for(ll l=1,r;l<=min(a,b);l=r+1) // 正常的数论分块
{
r=min(a/(a/l),b/(b/l));
ans+=(sum[r]-sum[l-1])*(a/l)*(b/l);
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
fir();
while(T--)
{
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
printf("%lld\n",sqr(x,y,z));
}
}