[海豹海边爆]AcWing199

原题链接:199. 余数之和 - AcWing题库

解题思路:本题题面给出的计算方式可以等价为n次k-(k/i)*i,i属于[1,n](即1到n的闭区间)。因为整型做除法时自动向0取整,再正整数范围内表现为向下取整,所以就是k%i等价于k减去k能被i整除的部分。那么题面的计算结果可以等价为以下公式:

n*k-\sum_{i=1}^{n}\left \lfloor k/i \right \rfloor*i

接着设g(x)为:

\left \lfloor k/\left \lfloor k/x \right \rfloor \right \rfloor

由以下推论可得向下取整时k/g(x)=k/x(论证部分不看也行,只要先记住这个结论)。

\because k/x\geqslant \left \lfloor k/x \right \rfloor

\therefore g(x)= \left \lfloor k/\left \lfloor k/x \right \rfloor \right \rfloor\geqslant \left \lfloor k/(k/x) \right \rfloor =x

\therefore \left \lfloor k/g(x) \right \rfloor \leqslant \left \lfloor k/x \right \rfloor

这是结论1

\because g(x)=\left \lfloor k/\left \lfloor k/x \right \rfloor \right \rfloor\leqslant k/\left \lfloor k/x \right \rfloor

\therefore \left \lfloor k/g(x) \right \rfloor=\left \lfloor k/\left \lfloor k/\left \lfloor k/x \right \rfloor \right \rfloor \right \rfloor\geqslant \left \lfloor k/(k/\left \lfloor k/x \right \rfloor) \right \rfloor =\left \lfloor k/k*\left \lfloor k/x \right \rfloor \right \rfloor =\left \lfloor k/x \right \rfloor

\left \lfloor k/g(x) \right \rfloor\geqslant \left \lfloor k/x \right \rfloor

这是结论2

由结论1与结论2可得

\left \lfloor k/g(x) \right \rfloor= \left \lfloor k/x \right \rfloor

由上述证明可以得到当i属于x到g(x)的闭区间内,k/i可以等价为k/x(基于整型运算的向下取整)。那么k/i*i=k/x*i,在x到g(x)的闭区间内,k/x是一个定值,而i每次固定增加1,那么在x到g(x)的闭区间内k/i*i的和可以用等差数列求和来计算。所用的等差数列求和公式为Sn=n*(a1+an)/2。

在题目中,n项为g(x)-x+1(因为是闭区间,不加一的话就变成了x到g(x)半开半闭区间内整数数量了)。而(a1+an)为(k/x*x+k/x*g(x)),因为k/x是两项相同的因数,所以可以把k/x提取到括号外面,变成(k/x)*(x+g(x))。

接下来只需要把n*k减去从x=1开始的x到g(x)的每一段和就能得到答案。即第一段为1到g(1),第二段为g(1)+1到g(g(1)+1),以此类推。

AC代码:

#include<bits/stdc++.h>
using namespace std;
long long n,k,ans;//n与k为题目原意,ans为答案
int main(){
	cin>>n>>k;
	ans=n*k;//先预设n*k,之后减去被除掉的部分 
	for(long long x=1,gx;x<=n&&x<=k;x=gx+1){//因为将k划分成数段,每一段都是闭区间[x,g(x)],所以后一段的起始点为前一段的g(x)+1;
		//初始值为1是因为从1开始,而x<=n是因为只需要计算到最后一项k%n就截止了,后面不需要计算。
		//而小于k是因为大于k的数余k,答案一定是k,不需要减去任何值 
		
		gx=min(k/(k/x),n);//如果按照g(x)公式计算超过n则右端点记到n,因为n之后的不需要计算 
		ans-=(k/x)*(x+gx)*(gx-x+1)/2;//等差数列前n项和公式 
	}
	cout<<ans;
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值