BZOJ 2705 Longge的问题 (欧拉函数)

来源:互联网 发布:大学生论文单片机课题 编辑:程序博客网 时间:2024/05/15 23:54

思路:

根据n的范围我们发现,就算是线性的扫一遍也不行(1e9的复杂度太高了)。
所以考虑一种logn或者sqrt(n)的方法;

我们知道这种题多半是不能按照题意直接做,而是要考虑每个gcd对答案的贡献。
考虑n的因子i,以i为gcd的对答案的贡献即为 gcd(,n)==i 的个数乘以i。又因为这道题要算的gcd中有一个是固定值,所以我们可以考虑将之转化为欧拉函数,即,个数为Euler(n/i)。在这个区间里和n/i互质的数,在分别乘以i后,一定就满足 gcd(,n)==i

同时,我们注意到每当我们找到n的一个因子i,我们其实同时还获得了n的另一个因子n/i,所以我们就可以通过这个,将复杂度降到sqrt(n)了。

注意,如果n是一个完全平方数,那么记得减去多加的那个因子的一遍贡献。

ps:

对了,这道题由于n太大了,只能每次求euler函数的值,而不能用线性筛o(n)的求。

#include<stdio.h>#include <iostream>#include<string.h>#include<math.h>#include<algorithm>#define eps 1e-8typedef long long int lli;using namespace std;const int maxn = 1e6+10;lli euler(lli n){    lli res = n,x=n;;    for(lli i = 2;i*i <= x; i++){        if(x%i==0){            res = res/i * (i-1);            while(x%i==0){                x/=i;            }        }    }    if(x!=1){        res = res/x*(x-1);    }    return res;}int main(){    lli p,q;        lli a,b;        scanf("%lld",&a);        lli ans = 0;        for(lli i = 1;i*i <= a;i++){            if(a%i == 0){                ans += i*euler(a/i)+ a/i*euler(i);                if(i*i==a) ans-= a/i*euler(i);            }        }        printf("%lld\n",ans);    return 0;}