整除分块

常搭配莫反食用。莫比乌斯反演笔记

P2261 余数求和

i=1nkmodin,k1e9

第一步:kmodi=kiki原式=i=1nki[ki].

第二步:原式=nki=1ni[ki]

然后问题就是怎么求 i=1ni[ki] 了。

直接枚举肯定是不行的,但是 [ki] 只有 k 种取值!

所以可以枚举 [ki] 的值。当这个值确定了,求出 [ki] 等于这个值的区间,然后在这个区间内就很简单了

因为取值只有 k 种,所以区间只有 k 个。我们每个区间 O(1),总复杂度就是 O(k)

怎么实现枚举取值?直接枚举又变成 O(k) 了,不行。

我们可以用类似双指针的方法,初始让指针 l=1,求出 [ki] 等于 [kl] 的最大的 i,令指针 ri。算完这段区间再令 lr+1,不断循环,直到 l 超过 n

一定要搞清楚 l 的上界,还有 r 是根据什么确定的!

怎么求 r?令 p=[kl]l 已知,这是可以 O(1) 求出的。我们求出 [ki]=pi 的范围,最大的 i 就是 r

[ki]=ppki<p+1kp+1<ikp

那么最大的 i 就很显然是 [kp] 了,也就是 kkl

以上就是所有思路,但是在代码上还有要注意的地方。

首先注意 k / (k / l) 的结果可能会超过 n 导致多算了一些东西,会 WA。所以要取 min

然后注意在 k / l = 0 的时候一定要跳出循环,不然上面 k / (k / l) 直接除 0 了。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

ll n, k;
ll ans = 0;

int main() {
    cin >> n >> k;
    ans = n * k;
    for (int l = 1, r = 0; l <= n; l = r + 1) {
        if (k / l != 0)
            r = min(k / (k / l), n);
        else
            break;
        ans -= 1ll * (k / l) * (l + r) * (r - l + 1) / 2;
    }
    cout << ans << endl;
    return 0;
}

整除分块一般用来处理 [x] 的问题。

posted @   FLY_lai  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示