CodeForces 616E(数学规律)

题意:

输入n, m(1  ≤  n, m  ≤  10^13),求 n%1 + n%2 + ... + n%m的值.

思路:

n%i = n - n/i(整除)*i;

所以 ∑(i=1, m) n%i 可以转化为 m*n - ∑(i=1, m) n/i*i;

易知:给定一个i,被n整除得c = n/i,另r = n/c,很容易可以得到n整除i到r范围内的所有数的值都是相同的,所以我们另i为下界,r为上界。那么我们求 ∑(i=1, m) n/i*i的时候就可以将它们分成若干组进行求和,另(n/i)相同的连续一段为一组。所以就可以利用等差数列求和公式对i到r范围内进行求和然后再乘上(n/i)就得到了这一段的ans,同理,其他段也是如此。


#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;
const LL mod = 1e9+7;
LL n, m, ans, up, sum, r;
int main()
{
	cin >> n >> m;
	ans = (n%mod)*(m%mod)%mod;
	up = min(n, m);	//当n>m时, 只能求m个模; n<m时, 只能求n个模 
	sum = 0;
	for(LL i = 1; i <= up; ++i)
	{
		r = min(n/(n/i), up);//如果上界超过up, 缩至up 
		LL a = i+r;	//准备求和公式 
		LL b = r-i+1;
		if(a&1) b /= 2;
		else a /= 2;
		sum = (sum + ((a%mod)*(b%mod)%mod)*(n/i)%mod)%mod;//获得此段的ans 
		i = r;
	}
	cout << (ans-sum+mod)%mod << endl;//防止mod之后相减变负数 
	return 0;
}


继续加油~
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值