POJ(POJ我真的无力吐槽,从来就没有AC过)
题意:有一个N*N的方阵,求站在方阵左下角,能看到多少个点?
分析:我习惯于把左下角的点看作(1,1).我们以初中数学的思维来看,本题就是斜率相同的直线上只能看到第一个点.我们想办法转换成计算机能理解的语言,例如对于两个点(4,6)和(2,3),这两个点在同一条直线上,所以我们只能看到(2,3).
通过这个例子你有没有发现,对于一个点(x,y),如果它能被我们看见,当且仅当满足(gcd(x,y)=1).其实很好理解,反证法,如果(gcd(x,y)!=1),我们设(d=gcd(x,y)),则这个点一定会被((x/d,y/d))这个点挡住.
所以本题我们实际上要求的就是对于一个y,有多少x与y互质,其中1<x<y,这不就是欧拉函数phi(y)吗?
然后像上面那样考虑的话,其实只考虑了方阵的一半,所以答案要乘2.
我们把第一行和第一列单独拎出来看,因为任意一个数与1的gcd都是1,而显然第一行和第一列,我们都只能看到第一个,也就是(1,2)和(2,1),然后(1,1)本身也能被看到,所以ans+=3.
所以我们只需要线性筛出n以内的欧拉函数,然后输出(ans=3+2*sum_{i=2}^{n}phi[i])
如果不会线性筛欧拉函数,戳这里
int n,m,ans;
int v[40005],prime[40005],phi[40005];
void get_phi(){//模板
for(int i=2;i<=40005;i++){
if(v[i]==0){
v[i]=i;prime[++m]=i;
phi[i]=i-1;
}
for(int j=1;j<=m;j++){
int cnt=prime[j]*i;
if(prime[j]>v[i]||cnt>40005)break;
v[cnt]=prime[j];
if(i%prime[j])phi[cnt]=phi[i]*(prime[j]-1);
else phi[cnt]=phi[i]*prime[j];
}
}
}
int main(){
get_phi();
n=read();
if(n==0||n==1){puts("0");return 0;}//特判
for(int i=2;i<n;i++)ans+=phi[i];
ans*=2;ans+=3;
printf("%d
",ans);
return 0;
}