天天看点

HDU 1695 (莫比乌斯入门)

题意:

从区间[1,B]选择一个x,从区间[1,D]选择一个数y,使得gcd(x,y)==k 的方案数。

莫比乌斯反演学习,感觉百度百科就非常好,其他的没有什么大的区别。

解题思路:

下面分两种办法进行求解。

首先,当然得用到莫比乌斯函数以及莫比乌斯反演了

1.采用莫比乌斯反演进行计算。

先将题意简化,那么

HDU 1695 (莫比乌斯入门)

就可以表达为:

HDU 1695 (莫比乌斯入门)

那么求前者的个数就相当于求后者的个数。

设:

HDU 1695 (莫比乌斯入门)

表示

HDU 1695 (莫比乌斯入门)

的个数

HDU 1695 (莫比乌斯入门)

表示

HDU 1695 (莫比乌斯入门)

 ( 

HDU 1695 (莫比乌斯入门)

 的倍数 )  的个数

HDU 1695 (莫比乌斯入门)

为什么要这么设呢,

一方面:因为当

HDU 1695 (莫比乌斯入门)

HDU 1695 (莫比乌斯入门)

就是所要求解的答案

另一方面:便于使用下面这两个公式:

HDU 1695 (莫比乌斯入门)

                             (1)

HDU 1695 (莫比乌斯入门)

                     (2)

对于公式(1)如果按照上面那种方法进行设,那么

HDU 1695 (莫比乌斯入门)

计算的结果就是(n的倍数的个数)。

并且你会发现

HDU 1695 (莫比乌斯入门)

                   (3)

再带入公式(2)先将(3)中

HDU 1695 (莫比乌斯入门)

中的

HDU 1695 (莫比乌斯入门)

换为

HDU 1695 (莫比乌斯入门)

,再令

HDU 1695 (莫比乌斯入门)

中的

HDU 1695 (莫比乌斯入门)

即可计算得到答案。

HDU 1695 (莫比乌斯入门)

令k==1即可算得。

2.采用莫比乌斯进行计算。

因为    

HDU 1695 (莫比乌斯入门)

那么将n换成

HDU 1695 (莫比乌斯入门)

,可得:

HDU 1695 (莫比乌斯入门)

那么,可以看出来:

HDU 1695 (莫比乌斯入门)
HDU 1695 (莫比乌斯入门)

直接套公式:

HDU 1695 (莫比乌斯入门)

然后枚举d,显然有

HDU 1695 (莫比乌斯入门)

于是将d提到前面去,则

HDU 1695 (莫比乌斯入门)

都是

HDU 1695 (莫比乌斯入门)

的倍数,化简一下,得

HDU 1695 (莫比乌斯入门)

那么现在既可以计算答案了,用整除分块再进行优化。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn=1e5+10;

bool vis[maxn];
int prime[maxn];
int mu[maxn];
int sum[maxn];

void get_mu(){
    memset(vis,0,sizeof vis);
    mu[1]=1;
    int tot=0;
    for(int i=2;i<maxn;i++){
        if(!vis[i]){
            prime[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot&&i*prime[j]<maxn;j++){
            if(i*prime[j]>maxn) break;
            vis[i*prime[j]]=1;
            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];
}

ll solve(int x,int y){
    int lit=min(x,y);
    ll ans=0;
    for(int i=1,j;i<=lit;i=j+1){
        j=min(x/(x/i),y/(y/i));
        if(j>lit) j=lit;
        ans+=ll(sum[j]-sum[i-1])*(x/i)*(y/i);
    }
    return ans;
}

int main(){
    get_mu();
    int t,a,n,b,m,k;
    scanf("%d",&t);
    int Case=1;
    while(t--){
        scanf("%d%d%d%d%d",&a,&n,&b,&m,&k);
        ll ans=0;
        if(k){
            if(n>m) swap(n,m);
            ans=solve(n/k,m/k)-solve(n/k,n/k)/2;
        }
        printf("Case %d: %lld\n",Case++,ans);
    }
    return 0;
}
//