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}^asum_{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} floor) * (lfloor{m/x} floor)

(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
",ans);
	}
}
原文地址:https://www.cnblogs.com/naruto-mzx/p/12973840.html