题意:
从区间[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;
}
//