区间和 离散化 模板题

数轴区间求和问题的算法解答


提示:以下是本篇文章正文内容

一、题目

假定有一个无限长的数轴,数轴上每个坐标上的数都是 0

现在,我们首先进行 n
次操作,每次操作将某一位置 x
上的数加 c

接下来,进行 m
次询问,每个询问包含两个整数 l
和 r
,你需要求出在区间 [l,r]
之间的所有数的和。

输入格式
第一行包含两个整数 n
和 m

接下来 n
行,每行包含两个整数 x
和 c

再接下来 m
行,每行包含两个整数 l
和 r

输出格式
共 m
行,每行输出一个询问中所求的区间内数字和。

数据范围
−109≤x≤109
,
1≤n,m≤105
,
−109≤l≤r≤109
,
−10000≤c≤10000

输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5

二、思路及代码

1.思路

离散化的思想。
大值域 但稀疏 映射到密集的顺序表中,使用二分去离散化。
保序, 通过值确认下标,所以就是二分的思想。
同时,角标要注意去重。不然相同位置上改变值会出现问题。
比如都在 a[8] 上 加 8 ,如果有两个 8 在 alls 的角标数组中不去重,会WA的。

在这里插入图片描述

picture by yxc just for learning ,no commercial use.

2.答案

代码如下:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
typedef pair<int, int> PII;

vector<PII> add, query;
vector<int> alls;

const int N = 300010;// n 个 x 下标 , n + m 个下标 总共最多只需要 3e5 个下标。
int a[N], s[N];

int find(int x) //离散化
{
    int l = 0, r = alls.size() - 1;
    
    while(l < r)
    {
        int mid = l + r >> 1;
        if(alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1;
}
int main()
{
    int n, m;
    cin >> n >> m;
    
    for(int i = 0; i < n; i ++)
    {
        int x, c;
        cin >> x >> c;
        add.push_back({x, c});
        alls.push_back(x);
    }
    
    for(int i = 0; i < m; i ++)
    {
        int l, r;
        cin >> l >> r;
        query.push_back({l, r});
        
        alls.push_back(l);
        alls.push_back(r);
    }
    // 去重的方法
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());
    
    for(auto item : add)
    {
        int x = find(item.first);
        a[x] += item.second;
    }
    //预处理前缀和 的思路
    for(int i = 1; i <= alls.size(); i ++)
    {
        s[i] = s[i - 1] + a[i];
    }
    // 求 前缀和
    for(auto item : query)
    {
        int l = find(item.first), r = find(item.second);
        cout << s[r] - s[l - 1] << endl;
    }
    
    
    return 0;
}

总结

Just Review.

<think>嗯,用户问的是C++数组中离散化模板的代码详解。首先我得确定自己对这个概念的理解是否正确。离散化,应该是指将一组可能分布很广的数据,映射到一个紧凑的区间内,通常是为了减少内存使用或者提高效率,比如在处理大范围的整数但实际数据稀疏时常用。比如,数据是1, 10000, 100000,离散化后可能变成0,1,2这样的连续索引。 接下来,我需要回想一下离散化的常规实现步骤。通常步骤包括排序、去重,然后使用二分查找来获取每个元素对应的新值。所以模板代码应该包括这几个部分。可能用户需要的是如何将原始数组转换为离散化后的索引,或者如何查询某个值在离散化后的位置。 然后,用户要代码详解,所以需要给出完整的示例代码,并逐行解释。可能还需要说明离散化的应用场景,比如处理区间问题,或者需要作为数组下标的情况,比如在树状数组或线段树中使用。 要注意到用户可能对STL中的sort、uniquelower_bound函数不太熟悉,所以解释这些函数的作用用法是要的。同时,离散化模板一般分为两种:保序的不保序的,但通常保序的更常见,也就是保持原数据的大小顺序。 另外,是否需要处理重复元素?去重是须的,所以在排序后使用uniqueerase来删除重复元素。这里需要注意unique返回的是新的逻辑结尾,所以需要用erase来实际删除后面的元素。 在编写示例代码的时候,可能需要一个具体的例子,比如给定一个数组,展示离散化的过程。例如,输入数组是{100, 1, 1000, 10, 100},排序后是1,10,100,100,1000,去重后是1,10,100,1000,然后每个元素对应的索引就是0,1,2,3。这样原数组离散化后就是2,0,3,1,2。不过要注意索引是从0还是1开始,不同情况可能需要调整。 此外,要提到离散化的优势,比如减少空间占用,加快访问速度,但也要注意离散化后查询的时间复杂度是O(log n),因为每次查询都要二分查找。 可能用户是在准备算法竞赛,比如处理线段树的问题,或者是需要统计频率的问题,所以需要确保代码高效且正确。在代码实现中,使用vector存储原始数据,然后处理后的离散化数组,以及如何通过lower_bound来快速查找索引。 最后,检查代码是否有常见错误,比如忘记去重,或者在查找时使用了错误的迭代器操作,比如没有减去begin()的迭代器导致索引错误。确保示例代码能够正确运行,并给出注释说明每个步骤的作用。</think>### C++ 离散化模板代码详解 离散化是一种将**稀疏分布的大范围数据**映射到**紧凑连续区间**的常用技巧,特别适用于处理离散点分布的统计问题。以下是完整模板代码及逐行解析: ```cpp #include <vector> #include <algorithm> // sort、unique、lower_bound using namespace std; // 离散化主函数(保序映射) vector<int> discretization(vector<int>& nums) { // 1. 复制原始数据(保持原数组不变) vector<int> temp = nums; // 2. 排序(O(n log n)) sort(temp.begin(), temp.end()); // 3. 去重(unique返回新逻辑终点,erase删除多余元素) temp.erase(unique(temp.begin(), temp.end()), temp.end()); // 4. 二分映射(通过lower_bound快速查找排名) vector<int> result; for (int num : nums) { int pos = lower_bound(temp.begin(), temp.end(), num) - temp.begin(); result.push_back(pos); // 映射结果从0开始 } return result; } ``` --- #### 关键步骤解析 1. **数据备份** ```cpp vector<int> temp = nums; ``` - 避免修改原始数据,保证函数无副作用 2. **排序处理** ```cpp sort(temp.begin(), temp.end()); ``` - 时间复杂度:O(n log n) - 示例:输入`[3, 1, 100, 2]` → 排序后`[1, 2, 3, 100]` 3. **去重操作** ```cpp temp.erase(unique(temp.begin(), temp.end()), temp.end()); ``` - `unique`将重复元素移动到容器末尾,返回新终点迭代器 - `erase`实际删除冗余元素 - 示例:排序后`[1,2,2,3]` → 去重后`[1,2,3]` 4. **二分映射** ```cpp int pos = lower_bound(temp.begin(), temp.end(), num) - temp.begin(); ``` - `lower_bound`查找第一个≥num的元素位置 - 通过迭代器相减得到数组下标 - 示例:num=3在`[1,2,3,100]`中的下标为2 --- #### 使用示例 ```cpp int main() { vector<int> raw = {100, 1, 1000, 10, 100}; // 原始数据 vector<int> discrete = discretization(raw); // 输出结果:2 0 3 1 2 for (int val : discrete) cout << val << " "; return 0; } ``` --- #### 应用场景 1. **区间统计问题** - 将坐标映射为连续索引,便于使用前缀、线段树等结构 2. **稀疏数据压缩** - 当数据范围大但实际使用值少时(如1e9范围只有1e5个点) 3. **离散事件处理** - 时间戳离散化后方便排序计算间隔 --- #### 时间复杂度分析 | 步骤 | 时间复杂度 | |------------|------------------| | 排序 | O(n log n) | | 去重 | O(n) | | 二分查找 | O(n log n) | | **总计** | **O(n log n)** | --- #### 扩展技巧 - **保序映射**:离散化后保持原数据大小关系(本模板实现方式) - **带权离散化**:在压缩时记录原始值与新索引的对应关系 - **离线处理**:先收集所有操作涉及的值,统一离散化后处理 > 离散化的核心在于**排序去重+二分查找**,该模板可处理`1e5`数量级的数据,是算法竞赛中的常用基础技巧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值