SPOJ - VLATTICE Visible Lattice Points(gcd(x,y,z)=1的对数/莫比乌斯反演)

题目链接:
SPOJ - VLATTICE Visible Lattice Points
题意:
一个n*n*n的方格,从最左下角(0, 0, 0)最多可以看到多少个点?(不被遮挡)包括方格内部。
分析:
假设能看到的点的坐标为 (x,y,z) 则必须满足: gcd(x,y,z)=1(0x,y,zn)
x=y=z=0 时是不成立的。
x,y,z 中有两个为0时,只有三种情况 (0,0,1),(0,1,0),(1,0,0) .
x,y,z 中有一个为0时,相当于求 gcd(x,y)=1(1x,yn) 的对数。
x,y,z 均大于0时,相当于求 gcd(x,y,z)=1(1x,y,zn) 的对数。
对于后两种情况用莫比乌斯反演解决。别忘了倒数第二种情况需要乘以3,因为1的位置有三种。

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

int prime_cnt;
int prime[MAX_N], mu[MAX_N], sum[MAX_N];
bitset bs;

void GetMu()
{
    bs.set();
    prime_cnt = 0;
    mu[1] = 1;
    for(int i = 2; i < MAX_N; ++i ) {
        if(bs[i] == 1) {
            prime[prime_cnt++] = i;
            mu[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];
            }else {
                mu[i * prime[j]] = 0;
                break;
            }
        }
    }
    for(int i = 1; i < MAX_N; i++) {
        sum[i] = sum[i - 1] + mu[i];
    }
}

ll solve(int n)
{
    int last;
    ll ans = 3; //当有两个数是0时,有3种情况
    for(int i = 1; i <= n; ++i) {
        last = n / (n / i);
        ll tmp =(ll)(n / i);
        //当x, y, z均大于0时
        ans += tmp * tmp * tmp * (sum[last] - sum[i - 1]);
        //当有1个数为0时,相当于gcd(x, y) = 1的对数,但是考虑0的位置有3种,所以要乘上3
        ans += tmp * tmp * (sum[last] - sum[i - 1]) * 3;
        i = last;
    }
    return ans;
}

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

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