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