SDOI 2008 仪仗队
洛谷 P2158 [SDOI2008]仪仗队
题目描述
作为体育委员,C君负责这次运动会仪仗队的训练。仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。 现在,C君希望你告诉他队伍整齐时能看到的学生人数。
输入格式
共一个数N
输出格式
共一个数,即C君应看到的学生人数。
输入输出样例
输入 #1复制
输出 #1复制
说明/提示
【数据规模和约定】
对于 100% 的数据,1 ≤ N ≤ 40000
题解:
一道欧拉函数的经典题。原题是POJ 3090 Visible Lattice Points
来看图说话:
观察这个图,我们首先发现,这个东西是关于直线\(y=x\)对称的。接下来我们观察遮挡,根据几何学中的相似知识(三角形相似),我们发现,如果有两个点的横纵坐标构成的两个三角形相似的话,那么那个较大的三角形(那个点)就会被挡住。
什么意思呢?我们发现,一个点可见的条件为:当且仅当\((x,y)\in N,x\not=y\)并且\(gcd(x,y)=1\),即横纵坐标互质。
因为这个图像的大小已知,并且这个图像是关于\(y=x\)对称的,那么我们只需要考虑半边的图像有多少点,给它乘二加三(因为有\((1,0),(1,1),(0,1)\))即可。
不需要双层循环进行枚举,只需要用一层循环枚举\(y\),因为是关于\(y=x\)对称的,所以\(x\in [1,y)\),那么,符合条件的\(x\)的数量就是\(y\)的欧拉函数\(\Phi (y)\)。
所以,答案为:
为什么是到\(n-1\)而不是到\(n\)呢?因为原点的坐标是\((0,0)\),而这个点不能被统计到答案中去(自己不能孤芳自赏),是从0计数的。
然后就简单了,一遍线筛筛选出欧拉函数数组,直接统计答案即可,复杂度是\(O(n)\)的。
关于欧拉函数的知识点,如有不太清楚的,敬请移步到本蒟蒻的这篇博客:
代码:
#include<cstdio>
using namespace std;
const int maxn=40010;
int phi[maxn],v[maxn],prime[maxn];
int n,cnt,ans;
void euler(int n)
{
cnt=0;
for(int i=2;i<=n;i++)
{
if(!v[i])
prime[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=n && i*prime[j]<=n;j++)
{
v[i*prime[j]]=1;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else
phi[i*prime[j]]=phi[i]*phi[prime[j]];
}
}
}
int main()
{
scanf("%d",&n);
if(n==1)
{
printf("0");
return 0;
}
euler(n);
for(int i=2;i<n;i++)
ans+=phi[i];
printf("%d",ans*2+3);
return 0;
}