题目链接
题意:让你在[1, b]找一个数x,在[1,d]中找到一个数y,并且满足gcd(x,y) == k,询问满足情况的对数。
我们可以把b/k, d/k。问题就转化成了求 i=1∑b/kj=1∑d/k[gcd(i,j)==1]。
看一眼数据范围,才1e5,我们可以枚举其中一个数(b或d)范围内除k的所有的可能因子,对于每个因子算出这个因子在另外一个数中互质的个数就行了。
因为(1, 3)(3,1)是相同的,所以要从两个数除k后较大的那个数枚举。
因为有case,可以把每个数的素因子筛出来,不然会T
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x)&(-x))
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+9;
int Case = 1, T = 0;
vector<int>ve[maxn];
bool isp[maxn];
ll query(int x, int y) {
ll t = ve[x].size(), res = y;
for(int i = 1; i < (1<<t); i++) {
ll temp = 1, cnt = 0;
for(int j = 0; j < t; j++) {
if((1<<j)&i) {
cnt++;
temp *= ve[x][j];
}
}
if(cnt&1) res -= y/temp;
else res += y/temp;
}
return res;
}
void solve(){
int a, b, n, m, k;
scanf("%d%d%d%d%d", &a, &n, &b, &m, &k);
ll res = 0;
if(!k) {
printf("Case %d: 0\n", ++T);
return;
}
n/=k;m/=k;
if(n>m) swap(n, m);
for(int i = 1; i <= m; i++) {
res += query(i, min(i, n));///询问在[1,min(i, n)]中与i互质的个数
}
printf("Case %d: %lld\n", ++T, res);
}
int main() {
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("/Users/hannibal_lecter/Desktop/code/in.txt", "r", stdin);
//freopen("/Users/hannibal_lecter/Desktop/code/out.txt","w",stdout);
#endif
for(int i = 2; i < maxn;i +=2)ve[i].push_back(2);
for(int i = 3; i < maxn; i+= 2) {
if(!isp[i]) {
for(int j = i; j < maxn; j += i) {
isp[j] = true;
ve[j].push_back(i);
}
}
}
scanf("%d", &Case);
while(Case--) {
solve();
}
return 0;
}