luogu P3455 [POI2007]ZAP-Queries |莫比乌斯反演
题目描述
密码学家正在尝试破解一种叫 BSA 的密码。
他发现,在破解一条消息的同时,他还需要回答这样一种问题:
给出 \(a,b,d\),求满足 \(1 \leq x \leq a\),\(1 \leq y \leq b\),且 \(\gcd(x,y)=d\) 的二元组 \((x,y)\) 的数量。
因为要解决的问题实在太多了,他便过来寻求你的帮助。
输入格式
输入第一行一个整数 \(n\),代表要回答的问题个数。
接下来 \(n\) 行,每行三个整数 \(a,b,d\)。
输出格式
对于每组询问,输出一个整数代表答案。
\(f(x)=\sum_{x=1}^a\sum_{y=1}^b [gcd(x,y)=d]\)
\(f(x)=\sum_{x=1}^{a/d}\sum_{y=1}^{b/d}[gcd(x,y)=1]\)
\(F(x)=\sum_{x|d}^n f(x)\)=\(\lfloor{n/x}\rfloor\) * \(\lfloor{m/x}\rfloor\)
\(f(x)=\sum_{x|d}^n u(d/x)*F(d)\)
#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
const int N=5e4+5,M=5e4;
int mu[N],pri[N],tot,n;
bool vis[N];
inline int read(){
int s = 0; char ch = getchar();
while(ch < '0' || ch > '9')ch = getchar();
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s ;
}
signed main(){
n=read();
mu[1]=vis[0]=vis[1]=1;
for(int i=2;i<=M;i++){
if(!vis[i]){ mu[i]=-1; pri[++tot]=i; }
for(int j=1;j<=tot&&pri[j]*i<=M;j++){
vis[pri[j]*i]=1;
if(i%pri[j]==0){ mu[pri[j]*i]=0; break; }
mu[pri[j]*i]=-mu[i];
}
}
for(int i=1;i<=M;i++)mu[i]+=mu[i-1];
int a,b,d;
while(n--){
a=read(),b=read(),d=read();
a/=d; b/=d;
int ans=0,lim=min(a,b);
for(int i=1,j=0;i<=lim;i=j+1){
j=min(a/(a/i),b/(b/i));
ans+=(mu[j]-mu[i-1])*(a/i)*(b/i);
}
printf("%d\n",ans);
}
}
不以物喜,不以己悲

浙公网安备 33010602011771号