洛谷 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 ot=y)并且(gcd(x,y)=1),即横纵坐标互质。
因为这个图像的大小已知,并且这个图像是关于(y=x)对称的,那么我们只需要考虑半边的图像有多少点,给它乘二加三(因为有((1,0),(1,1),(0,1)))即可。
不需要双层循环进行枚举,只需要用一层循环枚举(y),因为是关于(y=x)对称的,所以(xin [1,y)),那么,符合条件的(x)的数量就是(y)的欧拉函数(Phi (y))。
所以,答案为:
[sum_{i=2}^{n-1}Phi(i) imes 2+3
]
为什么是到(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;
}