题目链接:
SPOJ - VLATTICE Visible Lattice Points
题意:
一个n*n*n的方格,从最左下角(0, 0, 0)最多可以看到多少个点?(不被遮挡)包括方格内部。
分析:
假设能看到的点的坐标为 (x,y,z) 则必须满足: gcd(x,y,z)=1。(0≤x,y,z≤n)。
当 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(1≤x,y≤n) 的对数。
当 x,y,z 均大于0时,相当于求 gcd(x,y,z)=1(1≤x,y,z≤n) 的对数。
对于后两种情况用莫比乌斯反演解决。别忘了倒数第二种情况需要乘以3,因为1的位置有三种。
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <bitset>
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<MAX_N> 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种,所以要乘上3ans += 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;
}