区间合并与离散化处理

离散化及区间合并

离散化

典例

题目描述:假定有一个无限长的数轴,数轴上每个坐标上的数都是 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;
 }

 

posted @ 2021-04-05 19:19  how_you_make_me_feel  阅读(70)  评论(0编辑  收藏  举报