题意
从区间[1,B]选择一个x,从区间[1,D]选择一个数y,使得gcd(x,y)==k 的方案数。(1,3)和(3,1)算同一个。
题解
之前莫比乌斯反演总结:http://blog.csdn.net/williamsun0122/article/details/72510788
已经把所需知识介绍的很清楚了,接下来看看怎么应用。
这里我们设B < D(如果不是,你可以交换一下使之成立)
首先我们把所求变一下形:求gcd(x,y)==k可转变为求gcd(
xk,yk
)==1。
所以问题转变为在区间[1,
Bk
]和区间[1,
Dk
]中选择x,y满足gcd(x,y)==1的个数。因为在区间[1,
Bk
]中会有重复选择,所以我们需要把该区间中重复的去掉。(因为B < D,而小区间中必然会出现类似(1,2)和(2,1)的情况,所以该区间的总数应该是答案的两倍)
现在用莫比乌斯反演求解:
设f(k)表示gcd(x,y)==k的个数,也就是我们要计算的。(不好求)
再设F(k):表示gcd(x,y)==(k的倍数)的个数。 (好求)
以上(1<=x<=B,1<=y<=D)
下面(B=
Bk
,D=
Dk
)
由莫比乌斯反演的公式得到:
F(k)=∑k|df(d)
同时又由F函数的定义得到:(这个仔细想想就知道了)
F(k)=⌊Bk⌋⋅⌊Dk⌋
然后我们莫比乌斯反演就可以简单的求的f(k):
f(k)=∑k|dBμ(dk)F(d)=∑k|dBμ(dk)⋅⌊Bd⌋⋅⌊Dd⌋
按之前说的,在此时的区间([1<=x<= Bk ],[1<=y<= Dk ])中我们求的是gcd(x,y)==1,所以令上式中k的值为1即是答案。
接下来看看代码应该就懂了。
代码
#include <bits/stdc++.h>
using namespace std;
#define time_ (printf("%.6f\n", double(clock())/CLOCKS_PER_SEC))
typedef long long ll;
const int maxn = 1e5+5;
int mu[maxn],vis[maxn],prime[maxn];
void Moblus() //计算莫比乌斯函数的值
{
memset(vis,0,sizeof(vis));
int num=0;
mu[1]=1;
for(int i=2;i<maxn;i++)
{
if(!vis[i])
{
prime[num++]=i;
mu[i]=-1;
}
for(int j=0;j<num && prime[j]*i<maxn;j++)
{
vis[prime[j]*i]=1;
if(i%prime[j]==0)
{
mu[prime[j]*i]=0;
break;
}
else mu[prime[j]*i] = -mu[i];
}
}
}
int main()
{
int T;
int a,b,c,d,k;
Moblus();
scanf("%d",&T);
for(int ca=1;ca<=T;ca++)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(k==0)
{
printf("Case %d: 0\n",ca);
continue;
}
b /= k;
d /= k;
if(b>d) swap(b,d);
ll ans=0,repeat=0; //repeat/2为重复计数值
for(int i=1;i<=b;i++)
{
ans += (ll)mu[i]*(b/i)*(d/i);
repeat += (ll)mu[i]*(b/i)*(b/i);
}
printf("Case %d: %lld\n",ca,ans-repeat/2);
}
return 0;
}