题目
题目大意就是求在一个边长为N的正方体内,你站在(0,0,0)所能看到的所有点。
分析:这道题,我们可以分成三类来讨论.
第一类:坐标轴上的点
我们无论如何只能看见3个。
第二类:与原点相邻的三个表面的点
我们考虑其中一个表面,当且仅当在该点表面的坐标(x,y)满足gcd(x,y)=1时,可见。问题就转化为求1<=x,y<=n时,gcd(x,y)=1的对数,做法参加我的这篇博客。将所得答案*3即可。
第三类:其余情况
当且仅当(x,y,z),gcd(x,y,z)=1时可见。根据求二元组的F(i),f(i)推广,可得
F(i)=⌊n/i⌋3=∑i|df(d)=>f(i)=∑i|dμ(d/i)F(d)
然后求出f(1)即可。
三类情况的答案之和即为最终答案。
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 1000000
typedef long long LL;
int T,n,p[MAXN+10],mu[MAXN+10],sum[MAXN+10],pcnt;
bool f[MAXN+10];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
void prepare(){
int i,j;
mu[1]=sum[1]=1;
for(i=2;i<=MAXN;i++){
if(!f[i])
p[++pcnt]=i,mu[i]=-1;
for(j=1;p[j]*i<=MAXN;j++){
f[p[j]*i]=1;
if(i%p[j]==0){
mu[p[j]*i]=0;
break;
}
mu[p[j]*i]=-mu[i];
}
sum[i]=sum[i-1]+mu[i];
}
}
LL cal2(int n){
int i,last;
LL ret=0;
for(i=1;i<=n;i=last+1){
last=n/(n/i);
ret+=1ll*(sum[last]-sum[i-1])*(n/i)*(n/i);
}
return ret;
}
LL cal3(int n){
int i,last;
LL ret=0;
for(i=1;i<=n;i=last+1){
last=n/(n/i);
ret+=1ll*(sum[last]-sum[i-1])*(n/i)*(n/i)*(n/i);
}
return ret;
}
int main()
{
Read(T);
prepare();
while(T--){
Read(n);
printf("%lld\n",cal3(n)+cal2(n)*3+3);
}
}