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);
	}
}
posted @ 2020-05-27 16:17  白木偶君  阅读(170)  评论(0)    收藏  举报