洛谷P2303 Longge的问题

16 篇文章 0 订阅
4 篇文章 0 订阅

题目链接

题意:

给定一个整数 n n n,让我们求出表达式 ∑ i = 1 n g c d ( i , n ) \sum_{i=1}^ngcd(i,n) i=1ngcd(i,n)的值。

题解:

首先,我们可以发现这个最大公约数一定是 n n n的约数。因此我们可以进行分类,也就是枚举 n n n的约数。然后原来的表达式就可以改写成: ∑ d ∣ n d ∗ \sum_{d|n}d* dnd最大公约数为d的个数,也就是有多少个 x x x满足 g c d ( x , n ) = = d gcd(x,n)==d gcd(x,n)==d。这个个数该怎么求?
我们发现,如果有 g c d ( x , n ) = = d gcd(x,n)==d gcd(x,n)==d,那么 g c d ( x d , n d ) = = 1 gcd(\frac xd,\frac nd)==1 gcd(dx,dn)==1。 其实这个个数也就是 ϕ ( n d ) \phi(\frac nd) ϕ(dn)

于是上面的表达式就是 ∑ d ∣ n d ∗ ϕ ( n d ) \sum_{d|n}d*\phi(\frac nd) dndϕ(dn)

推出上面这个公式,已经可以进行求解了。时间复杂度为 O ( 1600 n ) O(1600\sqrt n) O(1600n )

不过时间复杂度可以进一步优化到 O ( n ) O(\sqrt n) O(n )

我们对上面的表达式进一步化简:

∑ d ∣ n d ∗ n d ∗ ∏ i = 1 k d ( 1 − 1 p i ) = n ∗ ∑ d ∣ n ∏ i = 1 k d ( 1 − 1 p i ) \sum_{d|n}d*\frac nd*\prod_{i=1}^{k_d}(1-\frac 1{p_i})=n*\sum_{d|n}\prod_{i=1}^{k_d}(1-\frac 1{p_i}) dnddni=1kd(1pi1)=ndni=1kd(1pi1)

怎么求这个积呢?

假设 n = p 1 α 1 p 2 α 2 … p k α k n=p_1^{\alpha_1}p_2^{\alpha_2}\dots p_k^{\alpha_k} n=p1α1p2α2pkαk,那么它的一个因子可以表示为:
d = p 1 β 1 p 2 β 2 … p k β k , 0 ≤ β i ≤ α i d=p_1^{\beta_1}p_2^{\beta_2}\dots p_k^{\beta_k},0\leq\beta_i\leq\alpha_i d=p1β1p2β2pkβk,0βiαi

那么我们可以对每个质因子依次判断,那么所有的约数可以表示出来:
( p 1 0 + p 1 1 + ⋯ + p 1 α 1 ) ( p 2 0 + p 2 1 + ⋯ + p 2 α 2 ) … ( p k 0 + p k 1 + ⋯ + p k α k ) (p_1^0+p_1^1+\dots +p_1^{\alpha_1})(p_2^0+p_2^1+\dots +p_2^{\alpha_2})\dots (p_k^0+p_k^1+\dots +p_k^{\alpha_k}) (p10+p11++p1α1)(p20+p21++p2α2)(pk0+pk1++pkαk)

那么 n n n的所有约数都可以从上面每个括号中选择一项组成。

那么这个 ∑ d ∣ n ∏ i = 1 k d ( 1 − 1 p i ) \sum_{d|n}\prod_{i=1}^{k_d}(1-\frac 1{p_i}) dni=1kd(1pi1)表达式的值可以根据每个质因子的选择情况求出,也就是:
( 1 + ( 1 − 1 p 1 ) + ⋯ + ( 1 − 1 p 1 ) ) ∗ ( 1 + ( 1 − 1 p 2 ) + ⋯ + ( 1 − 1 p 2 ) ) ∗ ⋯ = ∑ i = 1 k ( 1 + α i ( 1 − 1 p i ) ) (1+(1-\frac 1{p_1})+\dots +(1-\frac 1{p_1}))*(1+(1-\frac 1{p_2})+\dots +(1-\frac 1{p_2}))*\dots=\\\sum_{i=1}^k(1+\alpha_i(1-\frac 1{p_i})) (1+(1p11)++(1p11))(1+(1p21)++(1p21))=i=1k(1+αi(1pi1))

这样这道题目的就可以在 O ( n ) O(\sqrt n) O(n )解决。

实现细节见代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() 
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	ll n;
	cin >> n;
	ll ans = n;
	for (ll i = 2; i * i <= n; i++) {
		ll now = 0, p = i;
		if (n % p == 0) {
			while (n % p == 0) {
				n /= p;
				now++;
			}
			ans /= p;
			ans *= (p + now * p - now);
		}
	}
	if (n > 1) {
		ans /= n;
		ans *= (n + n - 1);
	}
	cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值