Codeforces 612D 前缀和处理区间问题

传送门:http://codeforces.com/problemset/problem/612/D

(转载请注明出处谢谢)

题意:

给出数字n和k,n表示接下来将输入n个在x轴上的闭区间[li,ri],找出被包含了至少k次的点,并求由这些点组成的连续区间的数目,并使该数目最小。输出该数目并将区间从左到右(x的正方向)输出。

比如样例1,给出区间[0,5],[-3,2],[3,8],那么被覆盖了至少两次的区间就是[0,2],[3,5],有两个。

 

解题思路:

     处理区间覆盖,一上来就想到了前缀和,普通前缀和处理的话,以数值为下标,然后开一个标记数组,每次对于区间[l,r],tmp[l]++,tmp[r+1]--,这样求前缀和数组s后,s[i]即为i被覆盖的次数,因此只需要如此操作一遍后,O(n)扫一遍,连续的s[x]>=k的,就为一个区间,记录输出即可。

     随后注意到,l和r的范围在[-1e9,1e9],开不了数组,但n只为1e6,每次输入最多2e6个数字,因此采用离散化处理。将输入是数字排序后,扫一遍所有区间,每次用lower_bound来求它所在的位置,因为一定存在,所以得到的下标一定是该数字的第一个下标(如果有重复,则为第一个该数字的下标)。

     在运行样例时,发现出来的结果不对。留意到是因为此时运行出来的前缀和数组s[]和想要的不一样。一般情况下,前缀和处理区间覆盖问题时“tmp[l]++,tmp[r+1]--”中的l和r指的是数值,而离散化后指的是它的下标,在处理的时候就会发生冲突。

     为了解决这个冲突,只要给每个数字都多“复制”一次就可以了,因为此时tmp[r+1]中的r+1所指是一个“多余”的位置,就不会出现冲突的情况。(语言表述有点无力,下面以第一个样例为例)

     区间[0,5],[-3,2],[3,8],输入后为a[]={-3,0,2,3,5,8},然后按上述所说求前缀和数组为s[]={1,2,2,2,2,1}。复制后,a[]={-3,-3,0,0,2,2,3,3,5,5,8,8},前缀和数组为s[]={1,1,2,2,2,1,2,2,2,1,1},此时s数组中值为2的连续区间上的第一个数和第二个数(相同下标的a数组)恰好是具体区间,冲突解决。

       具体实现细节看代码。

 

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e6+10;
int n,k,cnt,a[N<<2];
int tmp[N<<2],s[N<<2];
struct seg{
    int l,r;
}sgt[N],res[N];
int main(){
    //freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&k)){
        cnt=0;
        for(int i=0;i<n;i++){
            scanf("%d%d",&sgt[i].l,&sgt[i].r);
            a[cnt++]=sgt[i].l,a[cnt++]=sgt[i].l;
            a[cnt++]=sgt[i].r,a[cnt++]=sgt[i].r;
        }
        sort(a,a+cnt);
        memset(tmp,0,sizeof(tmp));
        for(int i=0;i<n;i++){
            tmp[lower_bound(a,a+cnt,sgt[i].l)-a]++;
            tmp[lower_bound(a,a+cnt,sgt[i].r)-a+1]--;
        }
        s[0]=tmp[0];
        for(int i=1;i<cnt;i++)
            s[i]=s[i-1]+tmp[i];
        int tot=0,flag=0;
        for(int i=0;i<cnt;i++){
            if(s[i]>=k){
                if(!flag){
                    res[tot].l=a[i];
                    flag=1;
                }
            }
            else{
                if(flag){
                    flag=0;
                    res[tot++].r=a[i-1];
                }
            }
        }
        printf("%d\n",tot);
        for(int i=0;i<tot;i++)
            printf("%d %d\n",res[i].l,res[i].r);
    }
    return 0;
}
View Code

 

posted @ 2016-01-01 13:42  轶辰  阅读(663)  评论(0编辑  收藏  举报