整除分块入门

整除分块是数论中的一个技巧,个人认为最好的理解方法是根据板题/例题解释。下面直接放了三道例题。

例1

UVA11526 H(n)

题意

i=1nni

这道题的 nint 范围内的非负数。

举个例子, n=15 ,给个直观一点的表:

i 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
ni 15 7 5 3 3 2 2 1 1 1 1 1 1 1 1

把下面的所有的 ni 相加即答案。

思路

假设当前贡献值的左边界为 l ,并且假设对于左边界 l 存在一个右边界 r

那么能得到:nl=nl+1=nl+2=...=nl+k=...=nr1=nr 如果是暴力的话,这些同样的值就要加 rl+1 遍,稳稳的 TLE

所以我们考虑能不能通过确定了 l 求出 r ,然后拿区间长度 rl+1 乘上一个 nl

所以我们通过向下取整的性质得出:nlnr 根据不等式的性质可得:rnnl

rmax=nnl

然后每次当前的 l 等于上一次的 r1 即可。

时间复杂度

O(n)

因为 ni 这玩意儿的集合大小最多为 2n

分情况讨论, in 时集合大小最大为 nn<in 时最大为 n

第一种 i 最多只有 n 个;第二种是因为考虑 n/i<=n ,所以最多也只有 n 个。

而集合里每个元素对时间复杂度的贡献是是 O(1)

所以接下来的东西就显而易见了。

#include <bits/stdc++.h>
#define ll long long
#define L(i, a, b) for(int i = a; i <= b; i++)
#define R(i, a, b) for(int i = a; i >= b; i--)
using namespace std;
int T, n; ll ans;
int main(){
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n), ans = 0;
        for(int l = 1, r; l <= n; l = r + 1){
            r = n / (n / l);
            ans += (r - l + 1) * (n / l);
        }
        printf("%lld\n", ans);
    } 
    return 0;
}

例2

洛谷P2424

思路

每个正整数 i1n 中作为因子出现的次数为 ni 。所以问题转换成了求 i=1nini

那么对于 ni 相同的,利用等差数列求和对 lr 求和即可。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll h(const ll n){
    ll ans = 0;
    for(register ll l = 1, r; l <= n; l = r + 1){
        r = n / (n / l);
        ans += (l + r) * (r - l + 1) / 2 * (n / l);
    }
    return ans; 
}
int main(){
    ll l, r;
    scanf("%lld%lld", &l, &r);
    printf("%lld\n", h(r) - h(l - 1));
    return 0;
}

习题1

洛谷P2261

posted @   徐子洋  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示