题目链接: 点我跳转
题目大意:如下图,给出一个方阵,求角落里的人可以直接看到的人的数量
题目分析:
1.暴力算法
如果(x,y)位置的人可以被看到,那么(kx,ky)位置的人一定可以被看到,那么我们不难发现,当且仅当,x与y互质的时候可以被看到,否则,会被 ( x m , y m ) (\frac xm,\frac ym) (mx,my)挡住,(m是最大公约数),显然,我们可以发现,最为暴力的思路就是枚举(x,y),然后计算 g c d ( x , y ) gcd(x,y) gcd(x,y)是否为1来判断x与y是否互质。但是,,,显然这种算法的复杂度太高了。我们需要一种更为快捷的方法来计算互质。
2.欧拉函数
接下来是这道题的重点,欧拉函数是指:所有小于n的,并且和n互质的数的个数。
关于欧拉函数的推导过程这里就不细说了(有兴趣自行百度吧,挺有趣的)
下面给出欧拉函数的公式:
φ
(
n
)
=
n
∗
(
1
−
1
p
1
)
(
1
−
1
p
2
)
⋯
(
1
−
1
p
k
)
φ(n)=n*(1-\frac 1{p_1})(1-\frac 1{p_2})\cdots(1-\frac 1{p_k})
φ(n)=n∗(1−p11)(1−p21)⋯(1−pk1)
这个公式一定要记下,这个公式一定要记下,这个公式一定要记下
其中
p
i
p_i
pi是n的质因数,并且是不重复的例如
φ
(
12
)
=
12
∗
(
1
−
1
2
)
∗
(
1
−
1
3
)
=
4
φ(12)=12*(1-\frac 12)*(1-\frac 13)=4
φ(12)=12∗(1−21)∗(1−31)=4表示12有4个小于12互质的数1,5,7,11。
有了欧拉函数这等神器我们就可以轻松解决这道题了
3.筛法欧拉函数
顾名思义,用来统计一段区间上面的许多数的欧拉函数,要不然每个数都要做一次质因数分解。
还是利用了筛的思想,我们首先令所有数的欧拉值都是自己,对于质数p,欧拉值是p-1,(除了自己剩下的都和他互质),然后将所有以p为质因数的合数都乘以
(
1
−
1
p
)
=
(
p
−
1
)
/
p
(1-\frac 1p) = (p-1)/p
(1−p1)=(p−1)/p,这样就可以高效地计算区间上的欧拉函数了
int euler[40000+5];
void getEuler(int n){
memset(euler,0,sizeof(euler));
euler[1] = 1;
for(int i=2;i<=n;i++)
if(!euler[i]) //euler[i] = 0说明是一个质数
for(int j=i;j<=n;j+=i){
if(!euler[j])euler[j] = j; //euler[j] = 0只是说明还没赋值
euler[j] = euler[j] / i * (i-1);
}
}
下面是这道题的完整代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int euler[40000+5];
void getEuler(int n){
memset(euler,0,sizeof(euler));
euler[1] = 1;
for(int i=2;i<=n;i++)
if(!euler[i])
for(int j=i;j<=n;j+=i){
if(!euler[j])euler[j] = j;
euler[j] = euler[j] / i * (i-1);
}
}
int solve(int n){
int ans = 0;
for(int i=1;i<=n;i++){
ans += euler[i];
}
return ans*2+1;
}
int main(){
int n;
cin>>n;
if(n == 1){cout<<0;return 0;}
getEuler(n-1);
cout<<solve(n-1);
return 0;
}