ZOJ3435
题意略。
思路:
将每一个点的坐标 (x,y,z) 与 (1,1,1) 相减,得到向量 (x - 1,y - 1,z - 1) 我们实际上就是要求出 这样互质的三元组有多少对就行了。
我们把这个长方体分成3部分:
1.三条坐标棱 2.在坐标棱上的三个表面(除去坐标棱) 3.长宽高分别为[L - 1,W - 1,H - 1]的长方体
三条坐标棱我们只要开3枪,就可以贯穿这个部分,ans += 3。
至于2和3,我们可以用莫比乌斯的第二类反演来解决。
在用莫比乌斯反演时,如果我们一 一枚举因子,就会T,我们来考虑一个sqrt(n)的优化。
比如说19这个数:
19 / 1 = 19,19 / 2 = 9,19 / 3 = 6,19 / 4 = 4,19 / 5 = 3,19 / 6 = 3,19 / 7 = 2,19 / 8 = 2,19 / 9 = 2,19 /10 = 1,......,19 / 19 = 1
在进行反演时,我们必然是通过枚举分母,计算出F(i) = (L / i) * (W / i) * (H / i)。
然而可以发现,7,8,9只要枚举一个就行,10~19也只需要枚举一个就行,如果我们用sum[ i ] 来维护mu[ i ]的前缀和,
那么ans += mu[ 7 ] * 19 / 7 + mu[ 8 ] * 19 / 8 + mu[ 9 ] * 19 / 9 等价于 ans += (sum[ 9 ] - sum[ 6 ]) * 19 / 7。
我们知道对于任意一个数x >= a * b,它的这种小于它且可以产生不同b的a最多有2 * sqrt(x)个。
详见代码:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const LL maxn = 1000005; LL l,w,h; bool check[maxn]; LL prime[maxn]; LL mu[maxn],sum[maxn]; void mobius(){ memset(check,false,sizeof(check)); mu[1] = 1; sum[1] = 1; int tot = 0; for(LL 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]; } sum[i] = sum[i - 1] + mu[i]; } } int main(){ mobius(); while(scanf("%lld%lld%lld",&l,&w,&h) == 3){ LL ans = 0; LL len = min(l,min(w,h)),last; for(LL i = 1;i < len;i = last + 1){ LL a = (l - 1) / i,b = (w - 1) / i,c = (h - 1) / i; last = min((l - 1) / a,min((w - 1) / b,(h - 1) / c)); ans += (sum[last] - sum[i - 1]) * (a * b * c); } len = min(l,h); for(LL i = 1;i < len;i = last + 1){ LL a = (l - 1) / i,b = (h - 1) / i; last = min((l - 1) / a,(h - 1) / b); ans += (sum[last] - sum[i - 1]) * (a * b); } len = min(l,w); for(LL i = 1;i < len;i = last + 1){ LL a = (l - 1) / i,b = (w - 1) / i; last = min((l - 1) / a,(w - 1) / b); ans += (sum[last] - sum[i - 1]) * (a * b); } len = min(w,h); for(LL i = 1;i < len;i = last + 1){ LL a = (w - 1) / i,b = (h - 1) / i; last = min((w - 1) / a,(h - 1) / b); ans += (sum[last] - sum[i - 1]) * (a * b); } ans = ans + 3; printf("%lld\n",ans); } return 0; }