第一次写一篇蓝题的题解,好激动!!!
废话不多说,首先看Farey序列的定义,(F_n)为一个分数集合,{$a/b,0<a<b leq n $ 且 (gcd(a,b) = 1)}.
由(0 < a < b leq n) 和 (gcd(a,b) = 1),不难想到求的是(n)以内的互质数对的个数。
那么我们在思考一下,这个应该怎么求呢?
不难想出,(F_n = F_{n-1} + varphi(n)),其中,(varphi(n))指(n)范围内与(n)互质的数的个数,即(n)的欧拉函数值。
这个式子很简单,也很好理解,(n - 1)内的互质数对的个数加上(n)以内与(n)互质的数的个数就是(n)以内的互质数对的个数。
那么,照着这个式子写下去,我们就会得到这样一个式子:
(F_n= F_1+varphi(2)+varphi(3)+......+varphi(n))
(~~~~~~=F_0 +varphi(1)+varphi(2)+varphi(3)+......+varphi(n))
而很显然0以内的互质数对个数为0,所以(F_0 = 0);
那么我们最终会得到这样一个式子:
(F_n = sum_{i = 1}^nvarphi(i))
接下来,我们要做的就是把(1 sim n)内所有数的欧拉函数值全部推出来。
关于欧拉函数的求法以及线性推法,如果您还不太了解,可以看一下蒟蒻自己写的这篇博客我怎么会告诉你我在安利自己的博客呢。
Ps:这篇博客内容较多,关于欧拉函数的部分在最后。
再看这道题的数据范围:
(0 < n < 1000001)
很显然,对(1sim n)中每一个数质因数分解妥妥的会T到飞起,所以我们就需要第二种线性推法来求,然后在主函数中直接推一遍前缀和就好了。
上代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long // 懒人专用
using namespace std;
const int maxn=1000005;
int phi[maxn],prime[maxn],tot;
// phi[i]表示i的欧拉函数值,prime用于储存质数
void phi_table(int n)
{
phi[1]=phi[0]=0;
for(int i=2;i<=n;i++){
if(!phi[i])phi[i]=i-1,prime[++tot]=i;
for(int j=1;j<=tot&&i*prime[j]<=n;j++){
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
signed main() // #define int long long 后必备,主函数返回值只能为int
{
int n;
phi_table(maxn-5);//直接将数据范围内所有数的欧拉函数值求出来。
for(int i=1;i<=maxn-5;i++)
phi[i]+=phi[i-1]; //前缀和
while(scanf("%lld",&n)){
if(n==0)break;
printf("%lld
",phi[n]);
}
return 0;
}
// 注意!十年OI一场空,不开long long见祖宗!!!
如有任何问题,请私信本人,如果觉得不错,点个赞再走吧!