1. 程式人生 > >BZOJ 2818 Gcd(gcd(x,y)為素數/尤拉函式/莫比烏斯反演)

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 <stdio.h>
#include <cstring>
#include <algorithm>
#include <bitset> #include <cmath> using namespace std; typedef long long ll; const int MAX_N = 10000010; int prime_cnt, prime[MAX_N]; bitset<MAX_N> 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; }
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <bitset>
#include <iostream>
using namespace std;
typedef long long ll;
const int MAX_N = 10000010;

bitset<MAX_N> 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;
}