区间合并和离散化

区间合并

AcWing 803.区间合并

给定 n 个区间 [ li,ri ],要求合并所有有交集的区间。

注意如果在端点处相交,也算有交集。

输出合并完成后的区间个数。

例如:[1,3]和[2,6]可以合并为一个区间[1,6]。

输入格式

第一行包含整数n。

接下来n行,每行包含两个整数 l 和 r。

输出格式

共一行,包含一个整数,表示合并区间完成后的区间个数。

数据范围

1≤n≤100000
−109≤li≤ri≤109

输入样例:

5
1 2
2 4
5 6
7 8
7 9

输出样例:

3

思路:

按照区间左边界进行依次排序,挨个处理,然后判断右边界的关系:

假设当前区间[ l1,r1 ],下一个区间[ l2,r2 ],首先我们可以确定l1≤ l2 一定成立,那么这两个区间可能存在三种关系:

  1. r2≤r1,暗含l2≤r1,即包含关系,此时可以忽略该区间
  2. r2>r1&&l2≤r1,此时只要修改当前区间为[ l1,r2 ]即可
  3. r2>r1&&l2>r1,说明这两个区间毫无关系,当前区间已经可以算为合并后的一个新区间了,开始判断下一个合并区间。
#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;

vector<PII> merge(vector<PII> &segs)
{
    
    
    vector<PII> res;//初始下res为空
    if (segs.size()!=0){
    
    
        int l=segs[0].first,r=segs[0].second;
        for (int i = 1; i < segs.size(); ++i) {
    
    
            if (segs[i].first > r){
    
    
                res.push_back({
    
    l,r});
                l=segs[i].first; r=segs[i].second;
            }
            else r=max(r,segs[i].second);
        }
        res.push_back({
    
    l,r});  //把最后剩下的放进去。
    }
    return res;
}

int main() {
    
    
    int n,l,r;
    cin>>n;
    vector<PII> segs;
    for (int i = 0; i < n; ++i) {
    
    
        scanf("%d%d",&l,&r);
        segs.push_back({
    
    l,r});
    }
    sort(segs.begin(),segs.end());
    auto res=merge(segs);
    cout<<res.size()<<endl;
//    for (int i = 0; i < res.size(); ++i) {
    
    
//        cout<<res[i].first<<" "<<res[i].second<<endl;
//    }

    return 0;
}

离散化

其实,这里说的离散化更像是对于稀疏矩阵的一种新的存储方式,类似于映射吧。

扫描二维码关注公众号,回复: 12416644 查看本文章

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

思路:

这种题需要考虑的数组长度(1e9)特别大,也就是说需要开很大的数组才能做到求其前缀和,但是考虑到用到的数并没有那么多,只有1e5,中间有很多地方值都是0,可忽略。即如果开那么大的数组老老实实的相加求前缀和是很麻烦的,因为数组很稀疏,所以我们考虑将涉及到的位置(下标)根据其从小到大的顺序映射到一个新的数组中,

接下来要考虑到修改原数组中某一位置/下标的时,需要找到该位置对应新数组的下标,我们可以将输入数据时的数存入新数组,但是当查询时,一些位置下标可能没有存入我们的新数组中,这样就找不到,这样,一些用于查询的下标就也需要存入我们的新数组中,所以考虑到实际情况,我们用到的下标最多有n+2*m个,即3e5,要注意去重!(考虑到重复:输入数据(x,c)进行修改时的重复以及查询时下标(l,r)已在数组中存储过的重复,所以实际上可能没有3e5这么多)

​ 当我们要修改数轴上 下标 为k的值:将其加c,通过查找到x在数组a中的下标得到值x,即a[x]=k

​ num数组用于记录用到的数轴上用到的下标处的对应的值

​ 修改后num数组对应的位置不为0,那些用于查找但不修改的位置对应的仍为0,由于num数组还要用 于求前缀和,所以num数组从1开始存储,而a数组从0开始存储,所以查到的k实际还要再加1才能对 应num数组。(因为不涉及到重复元素,所以+1即可)

​ 即,修改完(加c后),num[k+1]+=c; 即num[a[x]+1]+=c;

还有一个问题,原坐标轴上的位置/下标存入新数组a后,怎么在新数组a中找到原位置呢?

我们通过二分法不断逼近找到原数值,并返回其在新数组中的下标。

这样的 思路 和桶排,是完全相反的思路。

vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于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; // 映射到1, 2, ...n
}

unique(alls.begin(), alls.end()) 是c++的一个函数,负责筛选出所有的重复的元素,并将其放在容器的后面,返回不重复时的最大下标:alls.begin()+k。

在得到下标后:alls.erase(alls.begin()+k, alls.end()); 即可去除重复元素。

二者合起来就是:alls.erase(unique(alls.begin(), alls.end()), alls.end());

unique函数也可自己写:

数组中不重复的两个条件:(在数组有序的前提下)

  1. 是数组的第一个数
  2. a[i]!=a[i-1]
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;
}

最终代码实现:

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;
const int N=3e5+10;
int num[N];  //模拟数轴上的数值,默认全0
int s[N];//求num数组的前缀和

vector<int> a; //存储数值:用到的下标,和num数组对应,只不过num数组从1存储,a数组从0存储
vector<PII> add,b;//add数组用于n项+操作,b数组用于查询求和。

int find(int x)
{
    
    
    int l=0,r=a.size()-1;
    while (l<r){
    
    
        int mid=l+r>>1;
        if (a[mid]>=x) r=mid;
        else l=mid+1;
    }
    return l+1; //对应num数组从1存储,方便求前缀和
}

int main() {
    
    
    int n,m,x,c,l,r;
    cin>>n>>m;
    for (int i = 0; i < n; ++i) {
    
    
        scanf("%d%d",&x,&c);
        a.push_back(x);
        add.push_back({
    
    x,c});
    }
    for (int i = 0; i < m; ++i) {
    
    
        scanf("%d%d",&l,&r);
        a.push_back(l);  a.push_back(r);
        b.push_back({
    
    l,r});
    }
    sort(a.begin(),a.end());
    a.erase(unique(a.begin(),a.end()),a.end());
    for (auto item : add){
    
    
        x=find(item.first);
        num[x]+=item.second;
    }
    for (int i = 1; i <= a.size(); ++i)  s[i]+=s[i-1]+num[i];
    for (auto item : b){
    
    
        l=find(item.first),r=find(item.second);
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/HangHug_L/article/details/113151263