題意:
從區間[1,B]選擇一個x,從區間[1,D]選擇一個數y,使得gcd(x,y)==k 的方案數。
莫比烏斯反演學習,感覺百度百科就非常好,其他的沒有什麼大的差別。
解題思路:
下面分兩種辦法進行求解。
首先,當然得用到莫比烏斯函數以及莫比烏斯反演了
1.采用莫比烏斯反演進行計算。
先将題意簡化,那麼
就可以表達為:
那麼求前者的個數就相當于求後者的個數。
設:
表示
的個數
表示
(
的倍數 ) 的個數
為什麼要這麼設呢,
一方面:因為當
時
就是所要求解的答案
另一方面:便于使用下面這兩個公式:
(1)
(2)
對于公式(1)如果按照上面那種方法進行設,那麼
計算的結果就是(n的倍數的個數)。
并且你會發現
(3)
再帶入公式(2)先将(3)中
中的
換為
,再令
中的
即可計算得到答案。
令k==1即可算得。
2.采用莫比烏斯進行計算。
因為
那麼将n換成
,可得:
那麼,可以看出來:
直接套公式:
然後枚舉d,顯然有
于是将d提到前面去,則
都是
的倍數,化簡一下,得
那麼現在既可以計算答案了,用整除分塊再進行優化。
#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;
}
//