P2568 GCD

题意描述:

洛谷

给你一个 \(n\)\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n} [gcd(i,j)为质数]\)

数据范围:\(n\leq 10^7\)

sloution

首先我们很容易想出来怎么打暴力,我们可以对于每个 \(i\)\(j\) 都求一遍他们的 \(gcd\) 最后累计一下 \(gcd\) 为质数的就完成了。

可是这样得神仙时间复杂度 为 \(O(n^2)\), 对于这道题,我们肯定会 TLE。

我们就只能苦逼的想正解QAQ

然后就开始我们的推柿子时间QAQ

我们考虑这个柿子: \(gcd(x,y) = p\)

那么这个柿子可以写成 \(gcd(x' * p , y' * p) = p\)

提出 \(p\) 来就可以变成 \(gcd(x',y') = 1; x' = x / p ,y' = y / p\);

我们有一个推论就是:

\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n} [gcd(i,j) = 1]\) 不就相当于 \(\displaystyle 2 \times \sum_{i=1}^{N} \varphi(i) -1\)

证明:
因为 \(gcd(i,j) = gcd(j,i)\), 所以 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n} [gcd(i,j) == 1] = (2\times \sum_{i=1}^{n}\sum_{j=1}^{i} [gcd(i,j) == 1])-1\), (\(-1\) 是因为 \(i = j = 1\) 的时候算了两次)。
\(\displaystyle\sum_{j=1}^{i} [gcd(i,j) == 1]\) 等价于 \(1-i\) 内与 \(i\) 互质的个数,也就是 \(\varphi(i)\)
那么我们的式子可以写成: \(2\times \displaystyle\sum_{i=1}^{n} \varphi(i) - 1\)

因此对于每个 \(p\),他的贡献就是 \(\displaystyle 2\times \sum_{i=1}^{N\over p} \varphi(i) -1\).

那么有多少个这样的 \(p\) 呢???

其实 \(p\) 就是 \(N\) 以内的质数,对于每个 \(p\),求一下他的贡献,在相加,不就可以轻松AC了吗QAQ。。。

那么最后的总柿子就是

\(\displaystyle\sum_{p} (2\times \sum_{i=1}^{N\over p} \varphi(i))-1 ( p 是1~n的质数)\)

优化: 我们再求 \(p\) 的贡献时,不必要一个个的去求欧拉函数的值,我们可以考虑维护
一下前缀和。这样就会减少不少时间了...

最后附上我的代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e7+10;
long long n,ans = 0,cnt;
long long prime[N] ,phi[N], tot[N];
bool check[N];
void calc(int n){//边进行欧拉筛,边求欧拉函数
     memset(check,-1,sizeof(check));
     phi[1] = 1;
     for(int i = 2; i <= n; i++){
     	if(check[i]){
     		phi[i] = i-1;
     		prime[++cnt] = i;
     	}
     	for(int  j = 1; j <= cnt; j++){
     		if(i * prime[j] > n) break;
     		check[i*prime[j]] = 0;
     		if(i % prime[j] == 0){
     			phi[i*prime[j]] = phi[i] * prime[j];
     			break;
     		}
     		else{/积性函数性质
     			phi[i*prime[j]] = phi[i] * phi[prime[j]];
     		}
     	}
     }
     for(int i = 1; i <= n; i++) tot[i] = tot[i-1] + phi[i];//前缀和
}
int main(){
    scanf("%d",&n);
    calc(n);
    for(int i = 1; i <= cnt; i++){//枚举每个p
    	ans += 2 * tot[n/prime[i]] - 1;//求p的贡献
    }
    printf("%d\n",ans)
    return 0;
}
posted @ 2020-07-14 19:05  genshy  阅读(88)  评论(0编辑  收藏  举报