luogu P1829 [国家集训队]Crash的数字表格 / JZPTAB |莫比乌斯反演

题目描述

今天的数学课上,Crash 小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数 \(a\)\(b\)\(\text{lcm}(a,b)\) 表示能同时整除 \(a\)\(b\) 的最小正整数。例如,\(\text{lcm}(6, 8) = 24\)

回到家后,Crash 还在想着课上学的东西,为了研究最小公倍数,他画了一张 $ n \times m$ 的表格。每个格子里写了一个数字,其中第 \(i\) 行第 \(j\) 列的那个格子里写着数为 \(\text{lcm}(i, j)\)

看着这个表格,Crash 想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当 \(n\)\(m\) 很大时,Crash 就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash 只想知道表格里所有数的和  \(\bmod 20101009\) 的值。

输入格式

输入包含一行两个整数,分别表示 \(n\)\(m\)

输出格式

输出一个正整数,表示表格中所有数的和  \(\bmod 20101009\)的值。


#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e7+5,mod=20101009;
#define ll long long
#define int long long
bool vis[N];
int mu[N],pri[N],tot,n,m;

void Mobius(){
	vis[1]=mu[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i])pri[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&i*pri[j]<=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]=(i*i%mod*mu[i]%mod+mu[i-1])%mod;
}
inline int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=res*x%mod;
		x=x*x%mod; y>>=1;
	}
	return res;
}
inline ll S(int x){
	return ((x+1)*x>>1)%mod;
}
inline ll sum(int n,int m){
	ll ans=0;
	for(int d=1,j=0;d<=min(n,m);d=j+1){
		j=min(n/(n/d),m/(m/d));
		ll op=(S(m/d)*S(n/d))%mod;
		ans=(ans+(mu[j]-mu[d-1])%mod*op%mod+mod)%mod;
	}
	return ans;
}
signed main(){
	cin>>n>>m;
	Mobius();
	ll ans=0;
	for(int d=1,j=0;d<=min(n,m);d=j+1){
		j=min(n/(n/d),m/(m/d));
		ans=(ans+((j-d+1)*(d+j)%mod*ksm(2,mod-2)%mod)*sum(n/d,m/d)%mod)%mod;
	}
	cout<<ans<<endl;
}

猜你喜欢

转载自www.cnblogs.com/naruto-mzx/p/12978148.html