P3327 [SDOI2015]约数个数和 |莫比乌斯反演

题目描述

\(d(x)\)\(x\) 的约数个数,给定 \(n,m\),求

\(\sum_{i=1}^n\sum_{j=1}^md(ij)\)

输入格式

输入文件包含多组测试数据。
第一行,一个整数 \(T\),表示测试数据的组数。
接下来的 \(T\) 行,每行两个整数 \(n,m\)

输出格式

\(T\) 行,每行一个整数,表示你所求的答案。


\(d(ij)=\sum_{x|i}\sum_{y|i}[gcd(x,y)=1]\)

\(ANS=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|i}[gcd(x,y)=1]\)

\(ANS=\sum_{x=1}^n\sum_{y=1}^m \lfloor n/x\rfloor \lfloor m/y\rfloor[gcd(x,y)=1]\)

\(ANS=\sum_{x=1}^n\sum_{y=1}^m\sum_{d|gcd(x,y)}u(d) \lfloor n/x \rfloor \lfloor m/y\rfloor\)

\(令sum(n)=\sum_{x=1}^n\lfloor n/x \rfloor\)

\(ANS=\sum_{d=1}^nu(d)\sum_{x=1}^{n/d}\sum_{y=1}^{m/d}\lfloor n/x \rfloor \lfloor m/y\rfloor\)

\(ANS=\sum_{d=1}^nu(d)*sum(n/d)*sum(m/d)\)

\(O(n\sqrt n)\)预处理sum数组

\(O((n+T)\sqrt n)\)总复杂度

#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
const int N=5e5+10,M=5e4;
#define ll long long
#define int long long
inline int read(){
	int x=0; char c=getchar();
	while(c<'0'|c>'9')c=getchar();
	while('0'<=c&&c<='9'){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); }
	return x;
}
inline ll sum(int m){//sqrt
	ll ans=0;
	for(int i=1,j=0;i<=m;i=j+1){
		j=m/(m/i);
		ans+=(j-i+1)*(m/i);
	}
	return ans;
}
ll mu[N],pri[N],f[N],tot;
bool vis[N];
inline void mubius(int n){
	mu[1]=vis[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]){ pri[++tot]=i; mu[i]=-1; }
		for(int j=1;j<=tot&&pri[j]*i<=n;j++){
			vis[i*pri[j]]=1;
			if(i%pri[j])mu[i*pri[j]]=-mu[i];
			else { mu[i*pri[j]]=0; break;  }
		}
	}
	for(int i=1;i<=n;i++)mu[i]+=mu[i-1],f[i]=sum(i);
}

signed main(){
	int T=read(),n,m; 
	mubius(5e4);
	while(T--){
		n=read(),m=read();
		ll ans=0;
		int lim=min(n,m);
		for(int i=1,j=0;i<=lim;i=j+1){//sqrt
			j=min(n/(n/i),m/(m/i));
			ans+=(mu[j]-mu[i-1])*f[n/i]*f[m/i];
		}
		printf("%lld\n",ans);
	}
}
posted @ 2020-05-28 11:12  白木偶君  阅读(147)  评论(0编辑  收藏  举报