天天看點

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