题目描述
Byteasar the Cryptographer works on breaking the code of BSA (Byteotian Security Agency). He has alreadyfound out that whilst deciphering a message he will have to answer multiple queries of the form"for givenintegers aa , bb and dd , find the number of integer pairs (x,y)(x,y) satisfying the following conditions:
1≤x≤a ,1≤y≤b ,gcd(x,y)=d , where gcd(x,y) is the greatest common divisor of x and y ".
Byteasar would like to automate his work, so he has asked for your help.
TaskWrite a programme which:
reads from the standard input a list of queries, which the Byteasar has to give answer to, calculates answers to the queries, writes the outcome to the standard output.
FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。
输入输出格式
输入格式:The first line of the standard input contains one integer nn (1≤n≤50 000 ),denoting the number of queries.
The following nn lines contain three integers each: aa , bb and dd (1≤d≤a,b≤50 000 ), separated by single spaces.
Each triplet denotes a single query.
Your programme should write nn lines to the standard output. The ii 'th line should contain a single integer: theanswer to the i
i 'th query from the standard input
输入输出样例
2 4 5 2 6 4 3
3 2
我们设:
f(k)=i=1∑aj=1∑b[gcd(i,j)=k]
F(n)=n∣k∑f(k)=⌊na⌋⌊nb⌋
则可以由莫比乌斯反演可以推出:f(n)=n∣k∑μ(⌊nk⌋)F(k)
设完这两个函数之后,我们便惊喜的发现,Ans=f(d)
于是就直接开始推答案:
Ans=d∣k∑μ(⌊dk⌋)F(k)
枚举⌊dk⌋ ,设为t ,则
Ans=t=1∑min(a,b)μ(t)⌊tda⌋⌊tdb⌋
这时候,这个式子已经可以做到O(n) 的时间复杂度了,但是因为有多组数据,所以我们再用一下整除分块,这题就可以做到O(sqrt(n)) 了。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN (1<<16)
using namespace std;
long long k=0,prime[MAXN],mu[MAXN],sum[MAXN];
bool vis[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
void make(){
int m=MAXN-10;
mu[1]=1;
for(int i=2;i<=m;i++){
if(!vis[i]){
mu[i]=-1;
prime[++k]=i;
}
for(int j=1;j<=k&&prime[j]*i<=m;j++){
vis[prime[j]*i]=true;
if(i%prime[j]==0)break;
else mu[prime[j]*i]=-mu[i];
}
}
for(int i=1;i<=m;i++)sum[i]=sum[i-1]+mu[i];
}
void work(){
long long a,b,d,maxn,ans=0;
a=read();b=read();d=read();
maxn=min(a,b);
for(long long l=1,r;l<=maxn;l=r+1){
r=min(a/(a/l),b/(b/l));
ans+=(a/(l*d))*(b/(l*d))*(sum[r]-sum[l-1]);
}
printf("%lld\n",ans);
}
int main(){
int t=read();
make();
while(t--)work();
return 0;
}