数学体操 模运算


概述

求下列式子的值: ∑ i = 1 n n   m o d   i . \sum_{i=1}^n n\ mod\ i. i=1nn mod i.

其中, 1 ≤ n ≤ 1 0 9 . 1\leq n \leq 10^9. 1n109.

----------------------------------------
#思路
我们可以将 n   m o d   i n\ mod\ i n mod i写成 n − ⌊ n i ⌋ × i n-\lfloor \frac{n}{i}\rfloor \times i nin×i,那么原式就变成了: A n s = ∑ i = 1 n n   m o d   i = ∑ i = 1 n n − ⌊ n i ⌋ × i = ( ∑ i = 1 n n ) − ( ∑ i = 1 n ⌊ n i ⌋ × i ) . Ans=\sum_{i=1}^nn\ mod\ i=\sum_{i=1}^nn-\lfloor \frac{n}{i}\rfloor \times i=(\sum_{i=1}^nn)-(\sum_{i=1}^n\lfloor \frac{n}{i}\rfloor \times i). Ans=i=1nn mod i=i=1nnin×i=(i=1nn)(i=1nin×i).

整理一下: A n s = n 2 − ∑ i = 1 n ⌊ n i ⌋ × i . Ans = n^2-\sum_{i=1}^n\lfloor \frac{n}{i}\rfloor \times i. Ans=n2i=1nin×i.

那么现在的关键就是求后面那个 ∑ \sum 的值了。

我们可以观察一下 n = 11 n=11 n=11时,每个 i i i ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor in值:   i : 1 2 3 4 5 6 7 8 9 10 11 \ \quad i:\quad1\quad 2\quad 3\quad 4 \quad 5\quad 6 \quad7 \quad8 \quad9 \quad10\quad11  i:1234567891011 ⌊ n i ⌋ :   11 5 3 2 2 1 1 1 1 1 1 \lfloor \frac{n}{i}\rfloor:\ 11\quad 5\quad 3\quad 2\quad 2\quad 1\quad 1\quad 1\quad 1\quad 1\quad 1 in: 115322111111

观察细致的读者也许发现了,同一个 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor in值总是连续出现的,而且出现的次数似乎也有规律?

是的,令 i i i的贡献 ⌊ n i ⌋ = x \lfloor \frac{n}{i}\rfloor =x in=x,那么 x x x出现的次数为 ⌊ n x ⌋ − ⌊ n x + 1 ⌋ \lfloor \frac{n}{x}\rfloor-\lfloor \frac{n}{x+1}\rfloor xnx+1n次。

这个结论可以这么理解:贡献大于等于 x x x ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor in一共有 ⌊ n x ⌋ \lfloor \frac{n}{x}\rfloor xn个,贡献大于 x x x ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor in一共有 ⌊ n x + 1 ⌋ \lfloor \frac{n}{x+1}\rfloor x+1n个,两者相减即是贡献等于 x x x的个数了。

所以,我们可以分块计算贡献。对于 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor in相等的一个块,我们计算 ∑ ⌊ n i ⌋ × i \sum \lfloor \frac{n}{i}\rfloor \times i in×i可以提出公因数 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor in,剩下 ∑ i \sum i i是个等差数列,可以用求和公式直接计算。总复杂度均摊 O ( n ) . O(\sqrt n). O(n ).

----------------------------------------

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long
#define For(i,j,k) for(register int i=j; i<=(int)k; ++i)
#define Forr(i,j,k) for(register int i=j; i>=(int)k; --i)
#define INF 0x3f3f3f3f
using namespace std;

ll n, top, v, len, Ans;

int main(){
	scanf("%lld", &n);
	
	top = n;
	Ans = n*n;
	while(top >= 1){
		v = n/top;//计算当前位置的贡献.
		len = n/v - n/(v+1);//计算当前位置贡献出现的次数.
		Ans -= v*(2*top-len+1)*len/2;//更新答案.
		top -= len;//更新当前位置.
	}
	
	printf("%lld", Ans);
	return 0;
}

----------------------------------------

小结

本题重点在于将模运算转化成常见的四则运算,再观察贡献表(姑且这么叫吧)找出每个数的贡献所具有的规律,然后就可以快速计算了。

----------------------------------------

— — w r o t e   b y   m i r a c l e j z d . \mathscr ——wrote\ by\ miraclejzd. wrote by miraclejzd.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值