其他分享
首页 > 其他分享> > [SDOI2008]仪仗队

[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\n",ans);
    return 0;
}

标签:prime,phi,gcd,仪仗队,int,cnt,40005,SDOI2008
来源: https://www.cnblogs.com/PPXppx/p/10461030.html