码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ2818 GCD 【欧拉函数,线性筛】

时间:2016-08-01 21:07:27      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:

题目大意:

  给一个范围[1,n],从中找出两个数x,y,使得gcd(x,y)为质数,问有多少对(x,y有序)

解法:

  不难,欧拉函数练手题,可以定义集合P ={x|x为素数},那么我们枚举gcd(x,y)可能等于的情况,对于任意p∈P可以得到:gcd(k1·p,k2·p) = p,当且仅当gcd(k1,k2) =1;那么我们就只需要枚举所有的k1,k2了。不妨设k1>k2,那么给定k1,k2的个数就是phi(k1),因为有序,所以给phi*2,但是,这样是否漏算了呢?没错,漏算了(1,1),补上就行了,那么这个的答案就是计算下面那个表达式。

                              技术分享

  稍有常识的人就会看出,如果我们的坦克再前进一步(大雾)要算这个式子,复杂度是O(n^2)的,我们分析一下复杂度主要消耗再哪里。

  1.算phi

    如果我们用暴力公式,也就是技术分享的话,复杂度显然是很高的,全算出来高达(不是那个高达)O(n*sqrt(n)),这样的复杂度在1e7内是无法忍受的,所以我们 

    必须另外想办法,下面我引入三个很厉害东西:

      1.当p∈P时,phi(p) = p-1;

       2.如果i mod p = 0, 那么 phi(i * p)=p * phi(i),证明略,其实我也不会证

       3.若i mod p ≠0,  那么 phi( i * p )=phi(i) * ( p-1 ) ;

    有了这三个就容易写出线性的求euler函数了。

  2.二重循环

    里面那一重循环可以在外面递推乱搞,自己想,或者看我代码。

  那么这个的复杂度就降为了O(n);

3.下面是代码

  

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int flag[10000001],p[6000000],tot;
 4 int phi[10000001];
 5 void get_p(int end){
 6     for(int i=1;i<=end;i++)
 7         flag[i] = 1;
 8     for(int i=2; i<=end; i++){   
 9         if(flag[i]==1){
10             p[++tot]=i;
11             phi[i] = i-1;   
12         }
13         for(int j=1; j<=tot && p[j]*i<=end; j++){   
14             flag[p[j]*i]=0; 
15             if(i%p[j]==0){
16                 phi[i*p[j]] = p[j] * phi[i]; //欧拉函数的特性 
17                 break;   
18             }else phi[i*p[j]] = (p[j]-1) * phi[i];
19         }
20     }
21 }
22 long long f[10000001];
23 
24 int main(){
25     int n;
26     cin >> n;
27     get_p(n);
28     f[1] = 1;
29     for(int i=2;i<=n;i++){
30         f[i] = f[i-1] +phi[i]*2;
31     }
32     long long ans = 0;
33     for(int i=1;i<=tot;i++){
34         ans = ans+f[n/p[i]];
35     }
36     cout<<ans;
37     return 0;
38 }

 

BZOJ2818 GCD 【欧拉函数,线性筛】

标签:

原文地址:http://www.cnblogs.com/1-1-1-1/p/5727032.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!