【题意】
给定点(a,b,c)与(1,1,1)确定一个长方体,长方体边上及内部总共有a*b*c个点,问从(1,1,1)总共能看到多少个点。
【题解】
莫比乌斯反演
这是《仪仗队》(学习推荐:《bzoj2190 [SDOI2008]仪仗队》)的进阶版,从两维升级到三维。
同理,我们要求 三个坐标最大公约数为1 的点。
对于立体图形,我们分成3部分来考虑(以坐标中0的个数来划分)
2个0:共3个
1个0:这是点在长方体三个表面的情况,共solve1(a,b)+solve1(a,c)+solve1(b,c)个
0个0:这是点在长方体内部的情况,共solve2(a,b,c)个
【总结】
F[t]表示 gcd(a,b,c)|t的情况数 容易得到F[t]=(a/t)*(b/t)*(c/t)
f[t]表示 gcd(a,b,c)=t的情况数 反演公式不变
我们要求f[1]的值
莫比乌斯反演不仅仅适用于求两个数的最大公约数为t的情况,也适用于求多个数的最大公约数为t的情况。
【代码】
给定点(a,b,c)与(1,1,1)确定一个长方体,长方体边上及内部总共有a*b*c个点,问从(1,1,1)总共能看到多少个点。
【题解】
莫比乌斯反演
这是《仪仗队》(学习推荐:《bzoj2190 [SDOI2008]仪仗队》)的进阶版,从两维升级到三维。
同理,我们要求 三个坐标最大公约数为1 的点。
对于立体图形,我们分成3部分来考虑(以坐标中0的个数来划分)
2个0:共3个
1个0:这是点在长方体三个表面的情况,共solve1(a,b)+solve1(a,c)+solve1(b,c)个
0个0:这是点在长方体内部的情况,共solve2(a,b,c)个
【总结】
F[t]表示 gcd(a,b,c)|t的情况数 容易得到F[t]=(a/t)*(b/t)*(c/t)
f[t]表示 gcd(a,b,c)=t的情况数 反演公式不变
我们要求f[1]的值
莫比乌斯反演不仅仅适用于求两个数的最大公约数为t的情况,也适用于求多个数的最大公约数为t的情况。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int tot=0,prime[200010];bool v[200010];
int mo[200010];
void get_mo(int n)
{
mo[1]=1;
memset(v,true,sizeof(v));
for(int i=2;i<=n;i++)
{
if(v[i]==true)
{
prime[++tot]=i;
mo[i]=-1;
}
for(int j=1;j<=tot&&i*prime[j]<=n;j++)
{
v[i*prime[j]]=false;
if(i%prime[j]==0)
{
mo[i*prime[j]]=0;
break;
}
else
{
mo[i*prime[j]]=-mo[i];
}
}
}
}
long long solve1(int x,int y)//gcd(x,y)==1的情况数
{
long long re=0;
if(x>y) swap(x,y);
for(int i=1;i<=y;i++)
{
re+=(long long)mo[i]*(x/i)*(y/i);
}
return re;
}
long long solve2(int x,int y,int z)//gcd(x,y,z)==1的情况数
{
long long re=0;
if(x>y) swap(x,y);
if(y>z) swap(y,z);
if(x>y) swap(x,y);
for(int i=1;i<=z;i++)
{
re+=(long long)mo[i]*(x/i)*(y/i)*(z/i);
}
return re;
}
int main()
{
get_mo(200000);
int a,b,c;
while(scanf("%d%d%d",&a,&b,&c)!=EOF)
{
a--;b--;c--;
long long ans=3;
ans+=solve1(a,b)+solve1(a,c)+solve1(b,c);
ans+=solve2(a,b,c);
printf("%lld\n",ans);
}
return 0;
}