[BZOJ 2226] [Spoj 5971] LCMSum

本文详细解析了求∑i=1nlcm(i,n)的算法,通过大力化公式推导,将问题转化为统计所有数的答案,实现O(nln(n))的复杂度,适用于洛谷、BZOJ、SPOJ等平台的竞赛题目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
BZOJ传送门
SPOJ传送门

题意翻译

∑ i = 1 n    l c m ( i , n ) \sum_{i=1}^{n} \; lcm(i,n) i=1nlcm(i,n)

1 ≤ T ≤ 300000 , 1 ≤ n ≤ 1000000 1\le T\le 300000, 1 \le n \le 1000000 1T300000,1n1000000

解题分析

大力化公式:
∑ i = 1 n l c m ( i , n ) = ∑ i = 1 n i n g c d ( i , n ) = ∑ d ∣ n ∑ i = 1 n [ g c d ( i , n ) = d ] i n d = n ∑ d ∣ n ∑ i = 1 n d [ g c d ( i , n d ) = 1 ] i \sum_{i=1}^{n}lcm(i,n) \\ =\sum_{i=1}^{n}\frac{in}{gcd(i,n)} \\ =\sum_{d|n}\sum_{i=1}^{n}[gcd(i,n)=d]\frac{in}{d} \\ =n\sum_{d|n}\sum_{i=1}^{\frac{n}{d}}[gcd(i,\frac{n}{d})=1]i i=1nlcm(i,n)=i=1ngcd(i,n)in=dni=1n[gcd(i,n)=d]din=ndni=1dn[gcd(i,dn)=1]i
考虑后面那个式子, 实际上就等于 ϕ ( n d ) × n d 2 \frac{\phi(\frac{n}{d})\times\frac{n}{d}}{2} 2ϕ(dn)×dn, ( n d ≠ 1 \frac{n}{d}\ne 1 dn̸=1)因为和 n d \frac{n}{d} dn互质的的数是成对存在的, 并且加起来正好为 n d \frac{n}{d} dn

因此我们直接 O ( n l n ( n ) ) O(nln(n)) O(nln(n))统计所有数的答案, O ( 1 ) O(1) O(1)回答即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 1000500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int pcnt;
int pri[MX], phi[MX];
ll val[MX], ans[MX];
bool npr[MX];
void get()
{
	phi[1] = 1, val[1] = 1;
	R int i, j;
	ll tar;
	for (i = 2; i <= 1e6; ++i)
	{
		if (!npr[i]) pri[++pcnt] = i, phi[i] = i - 1, val[i] = 1ll * phi[i] * i / 2;
		for (j = 1; j <= pcnt; ++j)
		{
			tar = 1ll * i * pri[j];
			if (tar > 1e6) break;
			npr[tar] = true;
			if (!(i % pri[j]))
			{
				phi[tar] = phi[i] * pri[j];
				val[tar] = 1ll * phi[tar] * tar / 2;
				break;
			}
			phi[tar] = phi[i] * phi[pri[j]];
			val[tar] = 1ll * phi[tar] * tar / 2;
		}
	}
	for (R int i = 1; i <= 1e6; ++i)
	for (R int j = i; j <= 1e6; j += i)
	ans[j] += val[i];
}
int main(void)
{
	get(); int T, n;
	in(T);
	W (T--)
	{
		in(n);
		printf("%lld\n", ans[n] * n);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值