zoukankan      html  css  js  c++  java
  • 【luogu P1712区间】题解

    【NOI2016】区间

    题目描述

    在数轴上有NN 个闭区间 [l_1,r_1],[l_2,r_2],...,[l_n,r_n][l1,r1],[l2,r2],...,[ln,rn] 。现在要从中选出MM个区间,使得这MM 个区间共同包含至少一个位置。换句话说,就是使得存在一个 xx ,使得对于每一个被选中的区间[l_i,r_i][li,ri] ,都有 l_i≤x≤r_ilixri 。

    对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间[l_i,r_i][li,ri] 的长度定义为r_i-l_irili ,即等于它的右端点的值减去左端点的值。

    求所有合法方案中最小的花费。如果不存在合法的方案,输出-11 。

    输入格式

    第一行包含两个正整数N,MN,M 用空格隔开,意义如上文所述。保证1≤M≤N1MN

    接下来NN 行,每行表示一个区间,包含用空格隔开的两个整数l_ili 和r_iri 为该区间的左右端点。

    N<=500000,M<=200000,0≤li≤ri≤10^9N<=500000,M<=200000,0liri109

    输出格式

    只有一行,包含一个正整数,即最小花费。

    输入输出样例

    输入
    6 3
    3 5
    1 2
    3 4
    2 2
    1 5
    1 4
    输出
    2

    这道题呢,题目说要用长度最大的区间减去长度最小的区间,正好本来序列就没什么顺序,那我们给区间排个序吧,这样可以方便统计答案。
    排完序做什么呢?一定是统计每个点出现的次数吧。对于这些个区间,修改区间上点出现的次数,线段树是常规操作吧。
    我们用线段树修改所有点出现的次数的最大值,这样,当枚举到i时,加上区间i对点出现次数的贡献,此时tree[1]如果等于m,这说明一个可能的答案产生了。
    我们枚举刚刚统计过的区间j,把他的贡献删掉,线段树区间减。
    看看tree[1]是否发生变化。
    如果tree[1]变小,那说明这是对这个答案有影响的长度最小的区间了。更新答案ans=min(ans,sec[i].len-sec[j].len)。
    想想为什么可以删掉j这个区间?因为i后面的区间一定比i长,减去j后产生的答案一定会更大,因此完全不用考虑j对i后面区间的影响。
    既然j被删掉了,那么下一个区间就是j+1了,用head标记这个,当下一个答案产生时,从head开始枚举。
    注意到原区间坐标很大,引入离散化是必要的。
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ls k<<1
    #define rs k<<1|1
    #define min(x,y) (x<y?x:y)
    #define max(x,y) (x>y?x:y)
    using namespace std;
    const int N=550000; 
    int n,m,ans=0x7fffffff;
    int temp[N<<4];
    int tree[N<<4],tag[N<<4];
    struct node{
        int l,r,len;
    }sec[N];
    inline bool cmp(node x,node y){
        return x.len<y.len;
    }
    inline void read(int &x){
        x=0;
        char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    }
    inline void updata(int k,int x,int y){
        int mid=x+y>>1;
        tree[ls]+=tag[k];
        tree[rs]+=tag[k];
        tag[ls]+=tag[k];
        tag[rs]+=tag[k];
        tag[k]=0;
    }
    inline void change(int k,int x,int y,int l,int r,int val){
        if(x>r||y<l) return;
        if(x>=l&&y<=r){
            tree[k]=tree[k]+val;
            tag[k]+=val;
            return;
        }
        if(tag[k]) updata(k,x,y);
        int mid=x+y>>1;
        change(ls,x,mid,l,r,val);
        change(rs,mid+1,y,l,r,val);
        tree[k]=max(tree[ls],tree[rs]);
    }
    int main()
    {
        int i,j,x,y,t;
        int cnt=0;
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++){
            read(sec[i].l),read(sec[i].r);
            sec[i].len=sec[i].r-sec[i].l;
            temp[++cnt]=sec[i].l;
            temp[++cnt]=sec[i].r;
        }
        sort(sec+1,sec+n+1,cmp);
        sort(temp+1,temp+1+cnt);
        cnt=unique(temp+1,temp+1+cnt)-temp-1;
        for(i=1;i<=n;i++){
            sec[i].l=lower_bound(temp+1,temp+1+cnt,sec[i].l)-temp;
            sec[i].r=lower_bound(temp+1,temp+1+cnt,sec[i].r)-temp;
        }
        int head=0;
        for(i=1;i<=n;i++){
            x=sec[i].l,y=sec[i].r;
            change(1,1,cnt,x,y,1);
            if(tree[1]>=m)
                for(j=head;j<=i;j++){
                    x=sec[j].l,y=sec[j].r;
                    change(1,1,cnt,x,y,-1);//这时线段树的长度应该是1~cnt,而不是n
                    if(tree[1]<m){
                        head=j+1;
                        ans=min(ans,sec[i].len-sec[j].len);
                        break;
                    }
                }
        }
        if(ans==0x7fffffff) ans=-1;//不要忘记判断这个。
        printf("%d",ans);
    } 


  • 相关阅读:
    函数part1 初始函数
    文件操作 文件修改的两种方式
    文件操作part3 文件内指针的移动
    文件操作part2
    day08作业
    文件操作part1
    字符编码
    ORA-15124 数据库启动阶段报错
    将数据库从普通文件系统迁移到ASM中
    在ASM中移动数据文件
  • 原文地址:https://www.cnblogs.com/quitter/p/11674267.html
Copyright © 2011-2022 走看看