1. 程式人生 > >SPOJ VLATTICE Visible Lattice Points (莫比烏斯反演基礎題)

SPOJ VLATTICE 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

Date: 2010-07-29
Time limit: 1.368s
Source limit: 50000B
Memory limit: 1536MB
Languages: All except: NODEJS objc PERL 6 VB.net
Resource: own problem used for Indian ICPC training camp

題目大意:求在(0,0,0)到(n,n,n)這個立方體裡從(0,0,0)能看到多少個點

題目分析:(2,2,2)就看不到,因為被(1,1,1)擋住了,做過能量採集的都知道,就是求gcd(a, b, c) = 1的組數,其中1 <= a, b, c <= n,裸的莫比烏斯反演題,注意兩點,三個數軸上還有三點(0, 0, 1),(0 ,1, 0),(1, 0, 0),另外xoy面,yoz面,xoz面,三個面上還有一些點,這些都要單獨算,然後再加上立方體中不包括軸和麵的點,分塊求和優化10ms解決


#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
int const MAX = 1000005;
int mob[MAX], p[MAX], sum[MAX];
bool noprime[MAX];

int Min(int a, int b, int c)
{
    return min(a, min(b, c));
}

void Mobius()
{
    int pnum = 0;
    mob[1] = 1;
    sum[1] = 1;
    for(int i = 2; i < MAX; i++)
    {
        if(!noprime[i])
        {
            p[pnum ++] = i;
            mob[i] = -1;
        }
        for(int j = 0; j < pnum && i * p[j] < MAX; j++)
        {
            noprime[i * p[j]] = true;
            if(i % p[j] == 0)
            {
                mob[i * p[j]] = 0;
                break;
            }
            mob[i * p[j]] = -mob[i];
        }
        sum[i] = sum[i - 1] + mob[i];
    }
}

ll cal(int l, int r)
{
    if(l > r)
        swap(l, r);
    ll ans = 0;
    for(int i = 1, last = 0; i <= l; i = last + 1)
    {
        last = min(l / (l / i), r / (r / i));
        ans += (ll) (l / i) * (r / i) * (sum[last] - sum[i - 1]);
    }
    return ans;
}

ll cal(int l, int m, int r)
{
    if(l > r)
        swap(l, r);
    if(l > m)
        swap(l, m);
    ll ans = 0;
    for(int i = 1, last = 0; i <= l; i = last + 1)
    {
        last = Min(l / (l / i), m / (m / i), r / (r / i));
        ans += (ll) (l / i) * (m / i) * (r / i) * (sum[last] - sum[i - 1]);
    }
    return ans;
}

int main()
{
    Mobius();
    int T;
    scanf("%d", &T);
    while(T --)
    {
        int n;
        scanf("%d", &n);
        ll ans = 3;
        ans += (ll) cal(n, n, n);
        ans += (ll) cal(n ,n) * 3;
        printf("%lld\n", ans);
    }
}