题意翻译
题目描述
密码学家正在尝试破解一种叫 BSA 的密码。
他发现,在破解一条消息的同时,他还需要回答这样一种问题:
给出 a,b,d,求满足 1≤x≤a1,1≤y≤b1,且 gcd(x,y)=d 的二元组 (x,y) 的数量。
因为要解决的问题实在太多了,他便过来寻求你的帮助。
输入格式
输入第一行一个整数 n,代表要回答的问题个数。
接下来 n 行,每行三个整数 a,b,d
输出格式
对于每组询问,输出一个整数代表答案。
数据范围
1≤n≤500001 1≤d≤a,b≤50000
输入输出样例
输入 #1
2
4 5 2
6 4 3
输出 #1
3
2
一句话题意 求(displaystyle sum_{i=1}^nsum_{j=1}^m[gcd(i , j) = d])
其中 d 给定
化简一下式子
(large displaystyle sum_{i=1}^nsum_{j=1}^m[gcd(i,j)=d])
=(large displaystyle sum_{i=1}^{leftlfloorfrac{a}{d} ight floor}sum_{j=1}^{leftlfloorfrac{b}{d} ight floor}[gcd(i,j) = 1])
=(displaystyle sum_{i=1}^{leftlfloorfrac{a}{d} ight floor}sum_{j=1}^{leftlfloorfrac{b}{d} ight floor}sum_{p | i , j}mu(p))
=(displaystyle sum_{p=1}^{leftlfloorfrac{a}{d} ight floor} mu(p)sum_{i=1}^{leftlfloorfrac{a}{dp} ight floor}sum_{j=1}^{leftlfloorfrac{b}{dp} ight floor} 1)
到这基本就完了 , 对a , b 一起整除分块 , 在对mu[p] 求个前缀和。就没了
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 5e4+100;
#define int long long
int tot;
int vis[N] , prime[N] , mu[N];
long long sum[N];
void Init()
{
int maxn = 5e4;
mu[1] = 1; sum[1] = 1;
for(int i = 2 ; i <= maxn ; ++i)
{
if(!vis[i]) { prime[++tot] = i; mu[i] = -1; }
for(int j = 1 ; j <= tot && i * prime[j] <= maxn ; ++j)
{
vis[i * prime[j]] = 1;
if(i % prime[j]) mu[i*prime[j]] = -mu[i];
else { mu[i*prime[j]] = 0; break; }
}
sum[i] = sum[i-1] + mu[i];
}
return ;
}
signed main()
{
Init();
int n , a , b , d; scanf("%lld" ,&n);
while(n --)
{
scanf("%lld%lld%lld",&a,&b,&d); a /= d; b /= d;
if(a > b) swap(a , b);
long long ans = 0;
for(int p = 1 , r ; p <= a ; p = r + 1)
{
r = min(a , min(a / (a / p) , b / (b / p)));
ans = ans + (sum[r] - sum[p-1]) * (a / p) * (b / p); // a / p & b / p 是整除
}
printf("%lld
" , ans);
}
return 0;
}