题意:
给五个数1,b,1,d,k,在[a ,b]内选一个数x,在[c, d]内选一个数y,使得gcd(x,y) = k,求这样的数的对数。
解析:
可以用容斥来做,也可以莫比乌斯反演。
先上结论:
从 1到M 和 1到N (M < N)各选一个数,组成gcd(x,y) = 1 的个数是:
LL ans = 0;
for (int i = 1; i <= M; i++)
{
ans += mu[i] * (M / i) * (N / i);
}
尝试证明一下:
...(1d later)
尝试失败。
唯独找到的证明,只理解了一点:
http://www.isnowfy.com/mobius-inversion/
这题先把范围缩小到[1/k, b/k] , [1/k, d/k],
然后先算各从一段[1 ,M] , [1 , n ] 取到多少对,因为第一段重复算了一次,所以之后-掉第一段。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
using namespace std;
const int maxn = 1000000 + 10;
bool check[maxn];
int prime[maxn];
int mu[maxn];
void Moblus()
{
memset(check,false,sizeof(check));
mu[1] = 1;
int tot = 0;
for(int i = 2; i <= maxn; i++)
{
if( !check[i] )
{
prime[tot++] = i;
mu[i] = -1;
}
for(int j = 0; j < tot; j++)
{
if(i * prime[j] > maxn) break;
check[i * prime[j]] = true;
if( i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
Moblus();
int a, b, c, d, k;
int ncase;
int ca = 1;
scanf("%d", &ncase);
while (ncase--)
{
scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
if (k == 0)
{
printf("Case %d: 0\n", ca++);
}
else
{
b /= k;
d /= k;
if (b > d)
swap(b, d);
LL ans1 = 0;
for (int i = 1; i <= b; i++)
ans1 += (LL)mu[i] * (b / i) * (d / i);
LL ans2 = 0;
for (int i = 1; i <= b; i++)
ans2 += (LL)mu[i] * (b / i) * (b / i);
ans1 -= ans2 / 2;
printf("Case %d: %lld\n", ca++, ans1);
}
}
return 0;
}