看大神博客:点击打开链接
FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
inline int read(){
int x = 0,f = 1; char ch = getchar();
while(ch < '0'||ch > '9'){if(ch == '-')f=-1;ch = getchar();}
while(ch >= '0'&&ch <= '9'){x = x * 10 + ch -'0';ch = getchar();}
return x*f;
}
//
/*
算法:Mobius + 分块
题目:
对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d
(1<=d<=a,b<=50000)
*/
const int MAXN = 50000 + 10;
int tot;
int mu[MAXN+1],sum[MAXN+1],pri[MAXN+1];
bool mark[MAXN];
void get(){
mu[1] = 1;
for(int i = 2;i <= MAXN;++i){
if(!mark[i])pri[tot++] = i,mu[i] = -1;
for(int j = 0;j < tot&&i*pri[j] <= MAXN;++j){
mark[i*pri[j]] = 1;
if(i % pri[j]==0){mu[i*pri[j]] = 0; break;}
else mu[i*pri[j]] = -mu[i];
}
}
for(int i = 1;i <= MAXN;++i) //预处理前缀
sum[i] = sum[i-1] + mu[i];
}
int cal(int n,int m){
if(n > m) swap(n,m);
int ans = 0,pos;
for(int i = 1;i <= n;i = pos + 1){
pos = min(n/(n/i),m/(m/i)); //分块
ans += (sum[pos] - sum[i-1]) * (n/i) * (m/i);
}
return ans;
}
int main()
{
get();
int T = read();
while(T--){
int a = read(),b = read(),d = read();
printf("%d\n",cal(a/d,b/d));
}
return 0;
}