思路:由题意可知求gcd(i, j) = k 满足i属于区间[a,b],j属于区间[c, d]. 进而转化成求gcd(i/k, j/k) = 1 满足i属于区间[1, b/k],j属于区间[1, d/k].即求两区间内互素数的对数有多少。
欧拉函数可求出所有小于等于n并且与n互素的数的个数。假设b<d,两区间内所有小于等于b的互素对数可由前b项欧拉数获得,剩下的是求区间[b+1,d]与区间[1, b]互素的数的对数。利用容斥原理,对于区间[b+1,d]的每项元素j,假设都和区间[1, b]互素,在排除掉b中与j有相同因子的项数。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef __int64 lint;
const int MAX = 100009;
lint f[MAX],sum[MAX], tmp;
bool p[MAX]={0};
vector<int> v[MAX];
void init()
{
int i, j;
sum[1] = 1;
for(i = 0; i < MAX; i++)
f[i] = i;
for(i = 2; i < MAX; i++)
{
if(p[i] == 0)
{
f[i] = i-1;
v[i].push_back(i);
for(j = i + i; j <= MAX; j += i)
{
p[j] = 1;
f[j] *= i-1;
f[j] /= i;
v[j].push_back(i);
}
}
}
for (i = 2; i <= MAX; ++i)
sum[i] = sum[i-1] + f[i];
}
void dfs(int k, int j, int b, int lcm, int mark) {
if (k >= (int)v[j].size())
return;
int i;
for (i = k; i < (int)v[j].size(); ++i) {
if (v[j][i])
tmp += mark*b/(v[j][i]*lcm);
int t = v[j][i];
dfs(i+1, j, b, t*lcm, -mark);
}
}
int main()
{
init();
int i, j, ca, a, b, c, d, k;
lint ans;
scanf("%d", &ca);
for (i = 1; i <= ca; ++i) {
scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
if (k == 0) {
printf("Case %d: 0\n", i);
continue;
}
if (b > d)
swap(b, d);
b /= k;
d /= k;
ans = sum[b];
for (j = b+1; j <= d; ++j) {
tmp = 0;
dfs(0, j, b, 1, 1);
ans += b - tmp;
}
printf("Case %d: %I64d\n", i, ans);
}
return 0;
}