zoukankan      html  css  js  c++  java
  • [BZOJ4653][NOI2016]区间 贪心+线段树

    4653: [Noi2016]区间

    Time Limit: 60 Sec  Memory Limit: 256 MB

    Description

    在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。
     
    对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值。
     
    求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1。

    Input

    第一行包含两个正整数 n,m用空格隔开,意义如上文所述。保证 1≤m≤n
    接下来 n行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri 为该区间的左右端点。
    N<=500000,M<=200000,0≤li≤ri≤10^9

    Output

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

    Sample Input

    6 3
    3 5
    1 2
    3 4
    2 2
    1 5
    1 4

    Sample Output

    2
     
    题解:
    拿到这个题,应该都有一种贪心的冲动
    但是贪心贪的好不好就决定了能不能过……
    我们先拿一棵线段树来维护区间加和全局最大值操作,
    添加线段转化为区间+1,当全局最大值>=m时,我们就找到了一组合法解:虽然我们不知道到底是谁,但全局最大值已经>=m,线段集合中对应的m条线段就是合法解
    现在问题就变成了找谁是合法解。
    把线段按照长度排序,那么我们按照顺序添加这些线段,如果找到一组最优解,就先把找到的线段集合中最大值看成选择的最后那条线段,最小值看成第一条线段,然后把最小值向右逼近,看能不能更优,这样O(n)扫一遍,同时一直记录合法解的最小值,就能找到最后的答案了!
    代码见下:
     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <cstring>
     4 #include <map>
     5 using namespace std;
     6 const int N=500100,inf=0x7fffffff;
     7 map<int,int> mp;
     8 int stack[N<<1],top,cnt,n,m;
     9 struct line{int l,r,len;}seg[N];
    10 inline bool mt(const line &a,const line &b){return a.len<b.len;}
    11 struct node
    12 {
    13     node *ch[2];int maxn,mark;
    14     node(){ch[1]=ch[0]=NULL;mark=maxn=0;}
    15     inline void update(){maxn=max(ch[0]->maxn,ch[1]->maxn)+mark;}
    16 }*null=new node(),*root=null;
    17 inline node* newnode()
    18 {
    19     node *o=new node();
    20     o->maxn=o->mark=0;
    21     o->ch[0]=o->ch[1]=null;
    22     return o;
    23 }
    24 inline void add(node *&rt,int l,int r,int L,int R,int val)
    25 {
    26     if(rt==null)rt=newnode();
    27     if(L<=l&&r<=R){rt->maxn+=val,rt->mark+=val;return;}
    28     int mi=(l+r)>>1;
    29     if(L<=mi)add(rt->ch[0],l,mi,L,R,val);
    30     if(mi<R)add(rt->ch[1],mi+1,r,L,R,val);
    31     rt->update();
    32 }
    33 int main()
    34 {
    35     scanf("%d%d",&n,&m);int ans=inf;
    36     for(int i=1;i<=n;i++)
    37     {
    38         scanf("%d%d",&seg[i].l,&seg[i].r);
    39         seg[i].len=seg[i].r-seg[i].l;
    40         stack[++top]=seg[i].l,stack[++top]=seg[i].r;
    41     }
    42     sort(stack+1,stack+top+1);stack[0]=-inf;
    43     for(int i=1;i<=top;i++)
    44         if(stack[i]!=stack[i-1])mp[stack[i]]=++cnt;
    45     for(int i=1;i<=n;i++)seg[i].l=mp[seg[i].l],seg[i].r=mp[seg[i].r];
    46     sort(seg+1,seg+n+1,mt);root=newnode();
    47     for(int i=1,j=1;i<=n;i++)
    48     {
    49         add(root,1,cnt,seg[i].l,seg[i].r,1);
    50         while(root->maxn>=m)
    51         {
    52             ans=min(ans,seg[i].len-seg[j].len);
    53             add(root,1,cnt,seg[j].l,seg[j].r,-1);
    54             j++;
    55         }
    56     }
    57     printf("%d
    ",(ans==inf)?-1:ans);
    58 }
  • 相关阅读:
    栈和堆的区别【转】
    C++虚函数表解析(转)
    C++编码规范(转)
    全局变量的声明和定义 以及dll中全局变量的导出
    Sizeof与Strlen的区别与联系.
    利用事件对象实现线程同步
    创建互斥对象同步线程
    MFC GDI笔记 转
    ClientToScreen( )和ScreenToClient( )
    Visual C++线程同步技术剖析
  • 原文地址:https://www.cnblogs.com/LadyLex/p/7242808.html
Copyright © 2011-2022 走看看