bzoj3994/ SDOI2015 约数个数和(莫比乌斯函数

本文介绍了一种利用数论和算法技巧解决特定数学问题的方法,即求解给定N和M时,所有i从1到N,j从1到M的i*j的约数个数之和。通过引入莫比乌斯函数和分块思想,将复杂度降低到O(n√n + T√n)。

题目描述

设d(x)为x的约数个数,给定N、M,求 ∑i=1n∑j=1md(ij)\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}d(ij)i=1nj=1md(ij)

输入格式

输入文件包含多组测试数据。第一行,一个整数T,表示测试数据的组数。接下来的T行,每行两个整数N、M。

输出格式

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

输入输出样例
输入 #1

2
7 4
5 6

输出 #1

110
121
说明/提示
1<=N, M<=50000

1<=T<=50000

分析

首先有一个结论(别看我我不会证
d(ij)=∑x∣i∑y∣j[gcd(x,y)==1]d(ij) =\sum\limits_{x|i}\sum\limits_{y|j}[gcd(x,y)==1]d(ij)=xiyj[gcd(x,y)==1]
于是
ans=∑i=1n∑j=1md(ij)=∑i=1n∑j=1m∑x∣i∑y∣j[gcd(x,y)==1]=∑i=1n∑j=1m∑x∣i∑y∣j∑k∣x,k∣yμ(k)=∑k=1nμ(k)∑i=1⌊nk⌋⌊nkd⌋∑j=1⌊mk⌋⌊mkj⌋ans = \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}d(ij)= \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{x|i}\sum\limits_{y|j}[gcd(x,y)==1]=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{x|i}\sum\limits_{y|j}\sum\limits_{k|x,k|y}\mu(k)=\sum\limits_{k=1}^{n}\mu(k)\sum\limits_{i=1}^{{\tiny\left\lfloor\dfrac{n}{k}\right\rfloor}}\left\lfloor\dfrac{n}{kd}\right\rfloor\sum\limits_{j=1}^{{\tiny\left\lfloor\dfrac{m}{k}\right\rfloor}}\left\lfloor\dfrac{m}{kj}\right\rfloorans=i=1nj=1md(ij)=i=1nj=1mxiyj[gcd(x,y)==1]=i=1nj=1mxiyjkx,kyμ(k)=k=1nμ(k)i=1knkdnj=1kmkjm
g(n)=∑i=1n⌊ni⌋g(n) = \sum\limits_{i=1}^{n}\left\lfloor\dfrac{n}{i}\right\rfloorg(n)=i=1nin,于是
ans=∑k=1nμ(k)∗g(⌊nk⌋)∗g(⌊mk⌋)ans = \sum\limits_{k=1}^{n}\mu(k)*g(\left\lfloor\dfrac{n}{k}\right\rfloor)*g(\left\lfloor\dfrac{m}{k}\right\rfloor)ans=k=1nμ(k)g(kn)g(km),除法分块即可。
总复杂度 O(nn+Tn)O(n\sqrt{n}+T\sqrt{n})O(nn+Tn)

代码如下

#include <bits/stdc++.h>
#define N 50005
#define LL long long
using namespace std;
int g[N], mu[N], s[N], x[N], p[N], cnt, maxn = 50000;
LL ans, z = 1;
int main(){
	int i, j, n, m, t, l, r, T;
	mu[1] = 1;
	for(i = 2; i <= maxn; i++){
		if(!x[i]) x[i] = 1, p[++cnt] = i, mu[i] = -1;
		for(j = 1; j <= cnt; j++){
			t = i * p[j];
			if(t > maxn) break;
			x[t] = 1;
			if(i % p[j] == 0) break;
			mu[t] = -mu[i];
		}
	}
	for(i = 1; i <= maxn; i++) s[i] = s[i - 1] + mu[i];
	for(i = 1; i <= maxn; i++){
		for(l = 1; l <= i; l = r + 1){
			r = i / (i / l);
			g[i] += (r - l + 1) * (i / l);
		}
	} 
	scanf("%d", &T);
	while(T--){
		scanf("%d%d", &n, &m);
		if(n > m) swap(n, m);
		ans = 0;
		for(l = 1; l <= n; l = r + 1){
			r = min(n / (n / l), m / (m / l));
			ans += z * (s[r] - s[l - 1]) * g[n / l] * g[m / l];
		}
		printf("%lld\n", ans);
	}
	return 0;
} 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值