牛客 | 小G的约数引起的对于 整数分块 学习

整除分块是个啥:要求i=1nn/i 的值,这时候暴力需要O(n)的时间。由于这个区间是连续的,且’/'是向下取整,当i不能整除k时,n/i会等于最小的i(也就是区间最左边的值 L)除n的商。此时如果可以很快的找到这一个区间,那么就可以将时间复杂度降到O(n)。 接下来讲一下怎么去找这个区间:
假设 n = 20,然后打表

i 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
20/i 20 10 6 5 4 3 2 2 2 2 1 1 1 1 1 1 1 1 1 1

看到这个表不难发现规律,用20再去除以 (20/i) 就等于最后一个等于这个值的数,比如说 当i=7时,20/i=2,那么用20/(20/7) = 10, 这个时候10就是20/i等于2的最后一个值。可以利用这个特性,在区间最左边用O(1)的时间就可以计算出区间最右边的坐标。在这个区间内,所有的值都是相同的,所以找到这个区间后,直接用区间长度乘以单个数值就ok。

// Core Code
ll G(ll n) {
    ll cnt = 0;
    for (int l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l); // 区间最右边
        cnt += (n / l) * (r - l + 1);
    }
    return cnt;
}

Next:余数求和 要求i=1nk % i

i=1nk % i=i=1nki(k/i)=nki=1ni(k/i)

在每一段(L,R)中 k/i = k/L ,所以在相加的时候可以当作公因式提出来。i=1ni相当于一个等差数列。由等差数列求和公式可得: (R-L+1) * (L+R) / 2。

所以每一段(L,R)的和可以表示为 k/L * (R-L+1) * (L+R) / 2。

// RioTian 21/03/03
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll n, k, cnt;
int main() {
    cin >> n >> k;
    cnt = n * k;
    for (ll l = 1, r; l <= n; l = r + 1) {
        if (k / l != 0)
            r = min(n, k / (k / l));
        else
            r = n;
        cnt -= (k / l) * (r - l + 1) * (l + r) / 2;
    }
    cout << cnt << "\n";
    return 0;
}
posted @   RioTian  阅读(65)  评论(1编辑  收藏  举报
编辑推荐:
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
阅读排行:
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
· 5. Nginx 负载均衡配置案例(附有详细截图说明++)
历史上的今天:
2020-03-03 LeetCode 题解 | 70. 爬楼梯
2020-03-03 LeetCode 题解 | 242. 有效的字母异位词
2020-03-03 LeetCode 题解 | 面试题 10.01. 合并排序的数组
2020-03-03 PTA | 03-树2 List Leaves (25分)
2020-03-03 PTA | 03-树1 树的同构 (25分)
点击右上角即可分享
微信分享提示