[SDOI2008]仪仗队

洛谷

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;
}

原文地址:https://www.cnblogs.com/PPXppx/p/10461030.html