题目大意:
已知有一幅图N*N,小明站在左下角,他向图所示方向进行观看,问小明最多能看到多少人。
解题思路:
首先,小明的观察人数可以通过45°角对称分布,所以我们可以只看对角线以下能看多少人,然后向上乘个两倍就是总人数了,注意N=1 输出0,以及 小明可以看到(2,2)就可以了(坐标系以小明为原点 向上为y,向右为x)。
那么45°角下,小明能看到多少人呢?我们发现(8,4)会被(4,2)的挡住,(4,2)会被 (2,1)的挡住,通过这个关键的发现,我们认为坐标(a,b)中,若gcd(a,b)!=1,那么(a,b)能被观察到。
但是我们这里直接枚举坐标的复杂度为O(n^2),最后会TLE。这里用到欧拉函数phi(i),
phi(i)表示小于i的与i互质的个数。所以,我们最终只需要枚举x坐标,枚举的过程中通过phi(i)得到45°角以下的横坐标为i 能观察到phi(i)人。至于phi(i)怎么计算,这里不再赘述。
#include <bits/stdc++.h>
#define int long long
#define N 40000
using namespace std;
int n;
int phi[N+10],prime[N+10],tot,ans;
bool mark[N+10];
int32_t main(){
int nn;cin>>nn;
if(nn==1){
cout<<0<<endl;
return 0;
}
int i,j;
phi[1]=0;
for(i=2;i<=N;i++)//相当于分解质因式的逆过程
{
if(!mark[i])
{
prime[++tot]=i;//筛素数的时候首先会判断i是否是素数。
phi[i]=i-1;//当 i 是素数时 phi[i]=i-1
}
for(j=1;j<=tot;j++)
{
if(i*prime[j]>N) break;
mark[i*prime[j]]=1;//确定i*prime[j]不是素数
if(i%prime[j]==0)//接着我们会看prime[j]是否是i的约数
{
phi[i*prime[j]]=phi[i]*prime[j];break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);//其实这里prime[j]-1就是phi[prime[j]],利用了欧拉函数的积性
}
}
int sum=0;
for(int h=1;h<nn;h++){
if(h==1){
sum+=1;
}
sum+=phi[h];
}
sum*=2;
sum+=1;
cout<<sum<<endl;
return 0;
}