「BZOJ2190」[SDOI2008] 仪仗队

->点我进原题

[SDOI2008] 仪仗队


时空限制 1000ms / 128MB


Description

作为体育委员,C君负责这次运动会仪仗队的训练。仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

现在,C君希望你告诉他队伍整齐时能看到的学生人数。

Input

共一个数N。

Output

共一个数,即C君应看到的学生人数。

Sample Input

4

Sample Output

9

HINT

对于 100% 的数据,1 ≤ N ≤ 40000

思路

分析题目容易发现,除了\((1,0)、(0,1)、(1,1)\)三个人以外,一个人能被看到,当且仅当\(1 \leq x,y \leq N,x \neq y\)并且 \(gcd(x,y) = 1\)
\(1 \leq x,y \leq N,x \neq y\)中能看到的人关于\((0,0)\)\((N,N)\)的直线对称。我们可以考虑其中的一半,即\(1\le x< y \le N\)。换言之,对于每个\(2\le y\le N\),我们需要统计有多少个\(x\)满足\(1\le x< y\)并且\(gcd(x,y) = 1\)。这样的\(x\)的数量恰好就是\(\Phi(y)\)
综上所述,本题的答案就是\(3+2*\begin{matrix} \sum_{i=2}^N \Phi(i) \end{matrix}\)
在线性筛中,每个合数\(n\)只会被他的最小质因子\(p\)筛一次。我们恰好可以在此时执行上面两条判断,从\(\Phi(n/p)\)递推到\(\Phi(n)\)

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<iostream>
#define rg register
using namespace std;
inline int read(){
    rg int f=0,x=0;
    rg char ch=getchar();
    while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
    while(isdigit(ch))  x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}

const int N =40010;
int n,prime[N],vis[N],phi[N],tot,ans;
inline void euler(){
    for(rg int i=2;i<=n;++i){
        if(!vis[i]){
            vis[i]=prime[++tot]=i;
            phi[i]=i-1;     //和该数之前所有数都互质 
        }
        for(rg int j=1;j<=tot&&prime[j]<=vis[i];++j){
            if(i*prime[j]>n)    break;
            vis[i*prime[j]]=prime[j];
            phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
        }
    }
} 
signed main(){
    n=read()-1;
    phi[1]=1;
    euler();
    for(rg int i=2;i<=n;++i)    ans+=phi[i];
    if(n==1||n==0)  cout<<0;
    else    printf("%d",ans*2+3);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/horrigue/p/9666911.html
今日推荐