余数之和 数论分块
给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值。
例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7。
第一印象肯定是暴力了,但是本题目的数据量太大了,暴力会TLE,因此需要考虑另外的解法。
k % i = k − ⌊ k / i ⌋ × i k\%i=k-\lfloor k / i \rfloor \times i k%i=k−⌊k/i⌋×i,我们将每个取模运算都写成这样的形式,会得到,最终的结果是
n k − ∑ i = 1 n ⌊ k / i ⌋ × i nk-\sum_{i=1}^{n}{\lfloor k/i \rfloor}\times i nk−∑i=1n⌊k/i⌋×i
n k nk nk的值是直观可见的, ∑ i = 1 n ⌊ k / i ⌋ \sum_{i=1}^{n}{\lfloor k/i \rfloor} ∑i=1n⌊k/i⌋就是整出分块的内容,根据整出分块我们可以知道,里面有相当一部分连续的区间的值是相同的,而这些区间后续需要乘的数字(由于区间连续,所以数字是连续的)是公差为 1 1 1的等差数列,只要确定每个区间的左右端点,就可以再 O ( 1 ) O(1) O(1)的时间内求解这一个连续区间的答案,而数论分块告诉我们,每一个块的起点如果是 i i i,则终点是 ⌊ k / ⌊ k / i ⌋ ⌋ \lfloor k/ \lfloor k/i\rfloor\rfloor ⌊k/⌊k/i⌋⌋,但是要注意最后一个区间的右端点可能会小于这个值,因此需要取它和 n n n的最小值。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, k;
ll get(ll l, ll r){// 获取[l, r]内的和
int num = r - l + 1;
if (num & 1)
return (l + r) / 2 * num;
return num / 2 * (l + r);
}
int main(){
cin >> n >> k;
ll res;
res = n * k;
for (ll i = 1; i <= n;){
if (k / i == 0)// 此时与后面的数字相乘一定是0,而且后面的数字都是0,可以跳过
break;
ll l, r;
l = i;
r = k / i;
r = k / r;
r = min(r, n);// 防止r > n
res -= (k / i) * get(l, r);
i = r + 1;
}
cout << res << "\n";
return 0;
}
作者: correct
出处:https://www.cnblogs.com/correct/p/16548403.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!