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;
}

 

posted @ 2018-07-14 13:39  温和的提比略  阅读(113)  评论(0编辑  收藏  举报