• 区间合并与离散化处理


    离散化及区间合并

    离散化

    典例

    题目描述:假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。

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

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

    输入格式

    第一行包含两个整数 n 和 m。

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

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

    输出格式

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

    数据范围

    −10^9≤x≤10^9 1≤n,m≤10^5 −10^9≤l≤r≤10^9 −10000≤c≤10000

    输入样例:

     3 3
     1 2
     3 6
     7 5
     1 3
     4 6
     7 8

    输出样例:

     8
     0
     5

     

    code:

     //分析:
     //离散化:将数轴上离散的特定点映射到vector容器的连续下标上
     //这样可以大大减少空间耗费,因为数轴上的点的范围是 -1e9~1e9,按理说我们应该开辟一个2e9大小的数组空间,这样的大小确实令人揪心...
     //再者,区间和的得出是基于遍历数组的,也就是说我们要遍历2e9个位置,这耗时也是很恐怖的。
     //所以,我们可以通过分析问题本质可知:题目给出的最坏情况是 n=m=1e5.所以添加操作时涉及到的点的个数最大为1e5
     //询问操作(求区间和的询问)涉及到的点的个数最大为2e5,
     //所以:我们可以只开 1e5 + 2e5 +10(这10个是为了防止边界问题) = 3e5 + 10 个空间就足以应付问题了,
     //这是因为目标数据的稀疏性可以启发我们来做这样的优化
     //这对于 2e9 的空间优化有着质的变化 耗时也会随之降低一个层级
     //但是,这样的优化处理是基于一个成熟的映射思想的。
     
     
     #include<cstdio>
     #include<cstring>
     #include<iostream>
     #include<algorithm>
     using namespace std;
     const int N = 3e5 + 10;
     typedef pair<int,int> PII;
     
     //n是添加的次数,m是求区间和的次数,
     //a将存入我们数轴上的点经过一系列操作后的值-这里我们相邻索引存的可能是数轴上并不相邻的点
     //s存的就是a的前缀和啦
     int n,m,a[N],s[N];
     
     //alls存入的是我们所有操作所涉及到的点
     vector<int> alls;
     
     //add存入的是添加操作信息,元素类型为PII-键值对,前项为位置x,后项为在该位置上加的数c
     //query存入的是区间的左右端点,
     vector<PII> add,query;
     int find(int x){
         //二分查找数轴上的点在alls中的索引
         //二分模板 其1)
         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;
        }
         //这里我们要使用到前缀和思想来解题,所以返回的下标要+1.
         return l + 1;
     }
     int main(){
         scanf("%d%d",&n,&m);
         
         //读入添加操作信息
         for(int i=0;i<n;i++){
             int x,c;
             scanf("%d%d",&x,&c);
             //将x点放入alls中
             alls.push_back(x);
             
             //add容器更新
             add.push_back({x,c});
        }
         
         for(int i=0;i<m;i++){
             int l,r;
             scanf("%d%d",&l,&r);
             //涉及的点l,r放入alls中
             alls.push_back(l);
             alls.push_back(r);
             
             //将区间左右端点l,r放入query中
             query.push_back({l,r});
        }
         
         //接下来我们要对alls 排序 + 去重,因为一个点可能被多个操作涉及,所以alls中可能有相同的点,要去除
         sort(alls.begin(),alls.end());
         alls.erase(unique(alls.begin(),alls.end()),alls.end());
         
         //遍历添加操作
         for(auto item : add){
             //数轴上的数在alls中的索引,此步就已经完成了离散点的连续化了。
             //它在数轴上是离散的,但在容器索引里它可是相邻的噢
             int x = find(item.first);
             //容器中的索引作为前缀和的索引
             //再将我们的alls中的索引对应到a数组中,方便我们最后求前缀和
             a[x] += item.second;
        }
         
         //处理前缀和--简单的数学知识
         for(int i=1;i<=alls.size();i++) s[i] = s[i-1] + a[i];
         
         //再遍历求区间和的操作
         for(auto item : query){
             //查找区间的左右端点在alls中的索引位置
             int l = find(item.first), r = find(item.second);
             //前缀和的小小变形就可以求部分和啦~
             cout<<s[r] - s[l-1]<<endl;
        }
         return 0;
     }

    区间合并

    典例

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

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

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

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

    输入格式

    第一行包含整数 n。

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

    输出格式

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

    数据范围

    1≤n≤100000 -10^9≤li≤ri≤10^9

    输入样例:

     5
     1 2
     2 4
     5 6
     7 8
     7 9

    输出样例:

     3

    code:

     #include<cstdio>
     #include<cstring>
     #include<iostream>
     #include<algorithm>
     using namespace std;
     
     typedef pair<int,int> PII;
     const int N = 1e5 + 10;
     PII a[N];
     int n;
     int main(){
      scanf("%d",&n);
      for(int i=0;i<n;i++){
      int l,r;
      scanf("%d%d",&l,&r);
      a[i] = {l,r};
      }
      sort(a,a+n);
     
      int res = 1;
         //我们要维护的区间的左右端点
      int st = a[0].first,ed = a[0].second;
     
      for(int i=1;i<n;i++){
      int l=a[i].first,r=a[i].second;
      if(l==ed){
      ed = r;
      }else if(l<ed){
      if(r>ed){
      ed = r;
      }
      }else{
      res++;
      st = l;
      ed = r;
      }
      }
     
      cout<<res<<endl;
      return 0;
     }

     

  • 相关阅读:
    POI简单初识 Demo (资源来自网络本人属于收藏总结)
    spring学习笔记(6)装配Bean 的种类和区别 【资源来自网络 版权非本人】
    springboot相关
    前沿技术Hyperledger
    MAC版的IDEA快捷键
    JSONObject put,accumulate,element的区别
    1027
    position窗口居中
    http 请求详解大全
    关于Spring注解
  • 原文地址:https://www.cnblogs.com/yuanshixiao/p/14619160.html
Copyright © 2020-2023  润新知