Visible Lattice Points(莫比乌斯反演经典)

Visible Lattice Points

Consider a N*N*N lattice. One corner is at (0,0,0) and the opposite one is at (N,N,N). How many lattice points are visible from corner at (0,0,0) ? A point X is visible from point Y iff no other lattice point lies on the segment joining X and Y.

Input :
The first line contains the number of test cases T. The next T lines contain an interger N

Output :
Output T lines, one corresponding to each test case.

Sample Input :
3
1
2
5

Sample Output :
7
19
175

Constraints :
T <= 50
1 <= N <= 1000000

题意:

可以说此题是Visible Trees HDU2841(有详解)这道题目的改编,原题是求一个平面上即二维的,能看到多少并且是从(1,1)-(m,n),但是上面那道题我当时使用容斥做的,这道题则变成了三维的,问能看到多少点

分析:

如果大家先看看上面说的那道题目的话,就会明白,实际上是让我们找gcd(a,b,c) = 1的点有多少个,只要满足gcd(a,b,c) = 1就能看见

那么这道题就又和题目GCD hdu1695实质上也是求gcd(a,b) = 1的个数,而这道题我是用莫比乌斯反演做的

因此对于一般情况而言直接利用莫比乌斯反演可做

但是需要注意特殊情况,即在xyz直角坐标系中,在三个平面上时其实相当于是二维的,即有一个坐标为0,这样共有三种情况即在xy面,yz面,xz面,因此求处二维的乘3。

扫描二维码关注公众号,回复: 2913663 查看本文章

另一种特殊情况是在坐标轴上即有两个坐标为0,这样每个轴上就只能看到三个点,所以我们可以初始化sum=3,然后开始后面的累加

code:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
int prime[maxn],cnt;
int mu[maxn];
bool vis[maxn];
void init(){
    int N = maxn;
    memset(prime,0,sizeof(prime));
    memset(mu,0,sizeof(mu));
    memset(vis,0,sizeof(vis));
    mu[1] = 1;
    cnt = 0;
    for(int i = 2; i < N; i++){
        if(!vis[i]){
            prime[cnt++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < cnt && i * prime[j] < N; j++){
            vis[i*prime[j]] = 1;
            if(i % prime[j]) mu[i*prime[j]] = -mu[i];
            else{
                mu[i*prime[j]] = 0;
                break;
            }
        }
    }
}
int main(){
    init();
    int T;
    scanf("%d",&T);
    while(T--){
        ll n;
        scanf("%lld",&n);
        ll sum = 3;//在坐标轴上x,y,z
        for(ll i = 1; i <= n; i++){
            sum += mu[i] * (n / i) * (n / i) * (n / i + 3);
            //(n / i) * (n / i) * 3 相当于在面上的和
            //(n / i) * (n / i) *(n / i)相当于一般情况
        }
        printf("%lld\n",sum);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/81876769