Crash的数字表格 (莫比乌斯反演)

Crash的数字表格

Description

今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时被a和b整除的最小正整数。例如,LCM(6, 8) = 24。回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张N*M的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个4*5的表格如下: 1 2 3 4 5 2 2 6 4 10 3 6 3 12 15 4 4 12 4 20 看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod 20101009的值。

Input

输入的第一行包含两个正整数,分别表示N和M。

Output

输出一个正整数,表示表格中所有数的和mod 20101009的值。

Sample Input

4 5

Sample Output

122

数据规模和约定

100%的数据满足N, M ≤ 10^7。

题解

我们不妨设 N < M ,这样好做些

很容易就可以得出,我们想要的答案是 

ans=\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)

枚举lcm不方便,我们用gcd

ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{ij}{gcd(i,j)}

我们枚举gcd,上式就等于

ans=\sum_{d=1}^{n} \sum_{i=1}^{n}\sum_{j=1}^{m}\frac{ij}{d}(gcd(i,j)==d)=\sum_{d=1}^{n}\frac{1}{d}\sum_{i=1}^{n}\sum_{j=1}^{m}ij(gcd(i,j)==d)

我们把d分之一右边的式子拿出来处理,设

f(k)=\sum_{i=1}^{n}\sum_{j=1}^{m}ij(gcd(i,j)==k)

我们发现这个不好求,我们先求一个比较容易的,设

F(k)=\sum_{i=1}^{n}\sum_{j=1}^{m}ij(k|gcd(i,j))=\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}(ik)(jk)=k^2(\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}i)(\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}j)

我们发现右边括号里的就是 \sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}i=sum(1,\lfloor \frac{n}{k} \rfloor),可以用等差数列求和公式直接套,那么

F(k)=k^2(\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}i)(\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}j)=k^2sum(1,\lfloor \frac{n}{k} \rfloor)sum(1,\lfloor \frac{m}{k} \rfloor)

由于我们发现F(n)和f(n)有这样的关系:

F(n)=\sum_{n|d}f(d)

那么用一下莫比乌斯反演:

f(n)=\sum_{n|d}F(d)\mu (\frac{d}{n})=\sum_{n|d}d^2sum(1,\lfloor \frac{n}{d} \rfloor)sum(1,\lfloor \frac{m}{d} \rfloor)\mu(\frac{d}{n})

我们把它代入前面的ans:

\begin{align*} ans &=\sum_{d=1}^{n}\frac{1}{d}\sum_{d|x}x^2sum(1,\lfloor \frac{n}{x} \rfloor)sum(1,\lfloor \frac{m}{x} \rfloor)\mu(\frac{x}{d}) \\ &=\sum_{d=1}^{n}d\sum_{x=1}^{\lfloor \frac{n}{d} \rfloor}sum(1,\lfloor \frac{n}{xd} \rfloor)sum(1,\lfloor \frac{m}{xd} \rfloor)x^2\mu(x) \end{align*}

然后我们求d和x^2\mu(x)的前缀和,再分块就行。

这里可能很多人不理解这个分块是什么(我刚看题解的时候也不知道),我就解释一下

由于xd在一定的区间 [l , r] 中波动时,\lfloor \frac{n}{xd} \rfloor 的值是不变的,所以我们每次就直接算这个区间,中间不变,旁边的求d和x^2\mu(x)的前缀和就可以解决,这样可以大大减少时间复杂度,两重循环下来就是O(n)了。

CODE

        zxy AK了,%%%%%%%%        

#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#include<map>
#include<cmath>
#include<iostream>
#define MAXN 10000005
#define LL long long
#define rg register
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
//#pragma GCC optimize(2)
//#pragma G++ optimize(3)
//#define int LL
using namespace std;
inline int read() {
	int f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s == '-')f = -1;s = getchar();}
	while(s >= '0' && s <= '9') {x = x * 10 - '0' + s;s = getchar();}
	return x * f;
}
int zxy = 20101009;
int n,m,i,j,k,s,o;
int p[MAXN],cnt,mu[MAXN];
bool f[MAXN];
void sieve(int n) {
	mu[1] = 1;
	for(int i = 2;i <= n;i ++) {
		if(!f[i]) {
			p[++ cnt] = i;
			mu[i] = -1;
		}
		for(int j = 1;j <= cnt && i * p[j] <= n;j ++) {
			f[i * p[j]] = 1;
			if(i % p[j] == 0) {
				mu[i * p[j]] = 0;
				break;
			}
			else mu[i * p[j]] = -mu[i];
		}
	}
	for(int i = 1;i <= n;i ++) mu[i] = ((LL)mu[i] * i % zxy * i + mu[i-1]) % zxy;
	return ;
}
inline int dx(int a,int b) {
	return 1ll*(a + b) * (b - a + 1) / 2ll % zxy;
}
int solve(int n,int m) {
	LL ans = 0,ans2 = 0;int ii,jj;
	for(int i = 1;i <= n;i = ii + 1) {
		ii = min(n / (n / i),m / (m / i));
		int nd = n / i,md = m / i; ans2 = 0;
		for(int j = 1;j <= nd;j = jj + 1) {
			jj = min(nd / (nd / j),md / (md / j));
			ans2 = (ans2 + (0ll+mu[jj] - mu[j-1]) * dx(1,nd/j) % zxy * dx(1,md/j) % zxy) % zxy;
		}
		ans = (ans + 1ll * ans2 * dx(i,ii) % zxy) % zxy;
	}
	return (ans + zxy) % zxy;
}
signed main() {
	n = read();m = read();
	if(n > m) swap(n,m);
	sieve(m);
	printf("%d",solve(n,m));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值