题意:给定n*n*n的立方体,问从(0,0,0)点处能看到多少点,一个点能被看到当且仅当它与原点之间的连线上没有其他点。
思路:假设一个点(x,y,z)能被看到,那么gcd(x,y,z)一定为1,这是因为如果这三者有公因数,那么除以公因数后的这个点一定在它与原点之间的连线上,所以我们要求的等价于(0,n)中任取三点(可以相等),且这三者的gcd为1的点的数量。
这个问题就很类似经典莫比乌斯反演问题了,但是有0这个数不好办,对此,我们可以把整个立方体空间分成三部分
第一部分是(x>=1, y>=1, z>=1)的三维空间,对于这一部分我们可以用莫比乌斯反演来解决。
第二部分是(x=0, y>=1, z>=1),(x>=1, y=0, z>=1),(x>=1, y>=1, z=0)的区域,也就是三个等价的二维空间,也可以用莫比乌斯反演来求。
第三部分是三条坐标轴,对此我们直接在答案上加三即可。
这道题还有很重要的一点就是优化,处理莫比乌斯函数的前缀和然后分段求和可以大大优化运行时间,亲测不优化4970ms,优化后10ms,以前一直没重视这个优化,现在看来这个优化是很有必要的。
思路:假设一个点(x,y,z)能被看到,那么gcd(x,y,z)一定为1,这是因为如果这三者有公因数,那么除以公因数后的这个点一定在它与原点之间的连线上,所以我们要求的等价于(0,n)中任取三点(可以相等),且这三者的gcd为1的点的数量。
这个问题就很类似经典莫比乌斯反演问题了,但是有0这个数不好办,对此,我们可以把整个立方体空间分成三部分
第一部分是(x>=1, y>=1, z>=1)的三维空间,对于这一部分我们可以用莫比乌斯反演来解决。
第二部分是(x=0, y>=1, z>=1),(x>=1, y=0, z>=1),(x>=1, y>=1, z=0)的区域,也就是三个等价的二维空间,也可以用莫比乌斯反演来求。
第三部分是三条坐标轴,对此我们直接在答案上加三即可。
这道题还有很重要的一点就是优化,处理莫比乌斯函数的前缀和然后分段求和可以大大优化运行时间,亲测不优化4970ms,优化后10ms,以前一直没重视这个优化,现在看来这个优化是很有必要的。
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #include<ctime> #define eps 1e-6 #define LL long long #define pii pair<int, int> using namespace std; const int MAXN = 1000000+1000; bool check[MAXN+10]; int prime[MAXN+10]; int mu[MAXN+10], Sum[MAXN+10]; void Moblus() { memset(check,false,sizeof(check)); mu[1] = 1; int tot = 0; for(int i = 2; i <= MAXN; i++) { if( !check[i] ) { prime[tot++] = i; mu[i] = -1; } for(int j = 0; j < tot; j++) { if(i * prime[j] > MAXN) break; check[i * prime[j]] = true; if( i % prime[j] == 0) { mu[i * prime[j]] = 0; break; } else { mu[i * prime[j]] = -mu[i]; } } } for(int i = 1; i < MAXN; i++) Sum[i] = Sum[i-1] + mu[i]; } int main() { //freopen("input.txt", "r", stdin); int T; cin >> T; int n; Moblus(); while(T--) { scanf("%d", &n); LL ans = 0; int last; for(int i = 1; i <= n; i = last+1) { last = n/(n/i); ans += (LL)(Sum[last]-Sum[i-1])*(n/i)*(n/i)*(n/i); } for(int i = 1; i <= n; i = last+1) { last = n/(n/i); ans += (LL)(Sum[last]-Sum[i-1])*(n/i)*(n/i)*3; } cout << ans+3 << endl; } return 0; }