BZOJ 2818 Gcd(gcd(x,y)为素数/欧拉函数/莫比乌斯反演)

题目链接:
BZOJ 2818 Gcd
题意:
x[1,N],y[1,N]gcd(x,y)=(x,y)
分析:
对于一个素数 p ,如果 gcd(x,y)=p ,那么相当于 x[1,Np],y[1,Ny] (x,y) 的对数,又因为是有序对,需要乘以2,那么就是 Npi=12ϕ(i) ,其中 ϕ(i) i 的欧拉函数,所以需要记录欧拉函数的前缀和,但是其实这样是少算了的,因为 gcd(p,p)=p 就没算到。所以对于每个素数都要额外+1

#include 
#include 
#include 
#include 
#include 
using namespace std;

typedef long long ll;
const int MAX_N = 10000010;

int prime_cnt, prime[MAX_N];
bitset bs;
ll phi[MAX_N], sum[MAX_N];

void GetPhi()
{
    bs.set();
    prime_cnt = 0;
    for(int i = 2; i < MAX_N; i++) {
        if(bs[i]) {
            prime[prime_cnt++] = i;
            phi[i] = i - 1;
        }
        for(int j = 0; j < prime_cnt && i * prime[j] < MAX_N; j++) {
            bs[i * prime[j]] = 0;
            if(i % prime[j] == 0) {
                phi[i * prime[j]] = prime[j] * phi[i];
                break;
            }else {
                phi[i * prime[j]] = (prime[j] - 1) * phi[i];
            }
        }
    }
    for(int i = 1; i < MAX_N; i++){
        sum[i] = sum[i - 1] + 2 * phi[i];
    }
}

ll solve(int n)
{
    ll ans = 0;
    for(int i = 0; i < prime_cnt && prime[i] <= n; i++){
        ans += sum[n / prime[i]] + 1;
    }
    return ans;
}

int main()
{
    GetPhi();
    int n; 
    while(~scanf("%d", &n)){
        printf("%lld\n", solve(n));
    }
    return 0;
}

也可以是使用莫比乌斯反演。参考博客:http://blog.csdn.net/acdreamers/article/details/8542292

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int MAX_N = 10000010;

bitset bs;
int prime_cnt, prime[MAX_N / 100 * 7];
int mu[MAX_N], g[MAX_N];
ll sum[MAX_N];

void init()
{
    bs.set();
    prime_cnt = 0;
    mu[1] = 1;
    for(int i = 2; i < MAX_N; ++i) {
        if(bs[i]) {
            prime[prime_cnt++] = i;
            mu[i] = -1;
            g[i] = 1;
        }
        for(int j = 0; j < prime_cnt && i * prime[j] < MAX_N; ++j ){
            bs[i * prime[j]] = 0;
            if(i % prime[j]){
                mu[i * prime[j]] = -mu[i];
                g[i * prime[j]] = mu[i] - g[i];
            }else {
                mu[i * prime[j]] = 0;
                g[i * prime[j]] = mu[i];
                break;
            }
        }
    }

    sum[0] = 0;
    for(int i = 1; i < MAX_N; ++i) {
        sum[i] = sum[i - 1] + g[i];
    }


}

ll solve(int n, int m)
{
    int top = min(n, m), last;
    ll ans = 0;
    for(int i = 1; i <= top; i = last + 1) {
        last = min(n / (n / i), m / (m / i));
        ans += (ll)n / i * (m / i) * (sum[last] - sum[i - 1]);
    }
    return ans;
}

int main()
{
    init();
    int  n;
    while(~scanf("%d", &n)){
        printf("%lld\n", solve(n, n));
    }
    return 0;
}

你可能感兴趣的:(数论,莫比乌斯反演)