yxc_第一章 基础算法(三)_离散化

目录

1、pair

2、algorithm头文件

3、unique函数

 4、vector::iterator

 5、begin and end

6、erase函数

7、AcWing 802

(1)思路

(2)题解

1、pair

可以将两个数据合并成为一组数据

typedef pair<int, int> PII;//pair可以将两个数据合成一组
vector<PII> adds, query;//加值和询问都是成对

2、algorithm头文件

algorithm头文件主要函数

max返回两个元素中值最大的元素
min返回两个元素中值最小的元素
abs()返回元素绝对值
swap交换两个对象的值
reverse反转排序指定范围中的元素
sort排序

3、unique函数

#include<algorithm>        iterator unique(iterator it_1,iterator it_2);   //两个参数都是迭代器

unique的作用是“去掉”容器中相邻元素的重复元素,这里去掉要加一个引号,为什么呢,是因为它实质上是一个伪去除,它会把重复的元素添加到容器末尾,而返回值是去重之后的尾地址。

int num[10]={1,1,2,2,2,3,4,5,5,5};
int ans=unique(num,num+10)-num;

这时,返回的ans是5,而num中前5项就是1,2,3,4,5,(左闭右开!!!        返回值为去重后容器中不重复序列的最后一个元素的下一个元素。)

以上是C++自带的unique函数,接下来手写一个:

vector<int>:: iterator unique(vector<int> &a)
{
    int j = 0;
    for (int i = 0; i < a.size(); i ++ )
        if  (!i || a[i] != a[i - 1])
            a[j ++ ] = a[i];
    //a[0]~a[j - 1]为a中所有不重复的数
    
    return a.begin() + j;        //非重复序列最后一个元素的下一个位置
}

 4、vector<int>::iterator

定义向量迭代器
例如,vector<int>::iterator it  就是定义了一个名字叫it 的向量迭代器
for(it=v.begin();it!=v.end();it++)
     cout<<*it<<endl;

 5、begin and end

  • begin:返回指向容器第一个元素的迭代器

  • end:返回指向容器最后一个元素下一个位置的迭代器

begin和end返回的具体类型由对象是否是常量决定:

  • 如果对象是常量,begin和end返回const_iterator
  • 如果对象不是常量,返回iterator
vector<int> v1;
const vector<int> v2;
auto itr1 = v1.begin();//itr1的类型是vector<int>::iterator,返回指向容器第一个元素的迭代器
auto itr2 = v2.end();//itr2的类型是vector<int>::const_iterator,返回指向容器最后一个元素下一个位置的迭代器

6、erase函数

iterator erase(iterator first, iterator last);         左闭右开 [first,last) (sort函数也是左闭右开)

7、AcWing 802

假定有一个无限长的数轴,数轴上每个坐标上的数都是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)思路

n表示n个要加上值的下标,以及n个对应的加值。m表示m对区间端点。

add:存放n对数据,即下标x和对应的加值c

alls:存放n个加值的下标x(储存该下标x的空间对应的还有一个下标),以及2m个区间端点(l和r)

在开数组a时,由于数组a最大只需要(n+2m)的容量(此时为最坏情况,即加值下标与区间端点完全无重合),所以const int N = 300010;

可知,alls就是这道题目将会用到的所有下标。由于需要加值的下标x可能与区间端点l和r有重合,所以需要对alls数组去重。

// 去重
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());

接下来,构造find函数,传进来原下标x,返回存储x的空间的下标+1(这个+1是为了使得到的下标区间在[1,alls.size()],方便使用前缀和,因为前缀和的下标都是从1开始的)。

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;
}

遍历add数组,将数组元素的第一个值(即各加值的下标)依次传入find函数,得到其在alls数组中对应的下标+1(即为离散化后的下标),然后在a[N]数组中的对应位置加值c

// 处理插入
    for (auto item : add)
    {
        int x = find(item.first);
        a[x] += item.second;
    }

既然要去求区间的值之和,那么肯定要先求该区间的前缀和。随后,再去将l和r传入find函数依次对应到alls数组的下标(l,r,x离散化后的数值会发生变化,但是相对位置不会变),所以问题就转化为求离散化后的数组的前缀和之差,易解。

(2)题解

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

using namespace std;

typedef pair<int, int> PII;//pair可以将两个数据合成一组

const int N = 300010;
int n, m;
int a[N], S[N];

vector<int> alls;
vector<PII> adds, query;//加值和询问都是成对

//得到大于等于x的第一个下标加1,使得起始下标为1,便于前缀和
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;
        //当l = mid时,须int mid = l + r + 1 >> 1; 否则边界问题
        else l = mid + 1;
    }
    
    return r + 1;//或为r + 1,此时l = r;
}

int main()
{
    cin >> n >> m;
    
    //输入adds
    for (int i = 0; i < n; i ++ )
    {
        int x, c;
        cin >> x >> c;
        adds.push_back({x, c});//要用{},一组数据
        
        alls.push_back(x);
    }
    
    //输入query
    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);
    }
    
    //alls去重
    sort(alls.begin(), alls.end());//头文件algorithm
    alls.erase(unique(alls.begin(), alls.end()),alls.end());//unique函数头文件algorithm
    
    //将原下标离散化为alls下标
    for (auto item : adds)
    {
        int x = find(item.first);
        a[x] += item.second;//因为初始化为0,所以+=也行 错  //需要注意只写=的话ac不了
        //这里要考虑到item.first可能会出现重复,会覆盖之前的值
        //adds数组无序
    }
    
    //求前缀和
    for (int i = 1; i <= alls.size(); i ++ ) S[i] = S[i - 1] + a[i];
    
    //求m个区间和
    for (auto item : query)
    {
        int l = find(item.first), r = find(item.second);
        cout << S[r] - S[l - 1] << endl;
    }
    
    return 0;
}

 

  

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值