zoukankan      html  css  js  c++  java
  • NOI 2016 区间 解题报告

    题目描述

    在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。

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

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



    先考虑如何判断所选区间的方案合法?

    考虑线段树维护每个位置在当前方案下被覆盖了几次,然后查询总范围内被覆盖最多的点覆盖了几次,当这个值达到m则该方案合法;

    (区间加,区间最值,当然要离散,------用于check)

    然后考虑寻找最优情况;

    考虑,在方案中加入某区间对结果的影响,显然若该区间不比已有的最长区间长且不比最短区间短则她不会影响结果,

    于是,当我们确定好了一组最长、最短区间后,往这个大小范围内加什么区间都无所谓,

    于是,我们就把大小在该范围内的区间都加进方案好啦

    (一个贪心的策略,可以看出加的区间越多就越可能合法,而区间的数目并不是直接制约答案的东西,于是为了利用一组最长、最短区间最大价值,我们想出了上一行的做法)

    于是先把区间按大小排序吧;

    然后,我们把方案分为三个等级:

    1. 符合上文提到的贪心策略且合法的集合(可以是任意由排序后连续的区间构成的合法集合);
    2. 在1基础上,刚刚合法的集合(可以理解为——由排序后连续的区间构成的合法集合,但只要集合里最短|最长区间两者中有一个被踢出,该方案就非法了)
    3. 在2基础上,贡献最终结果的集合(就是2中最优者)

    显然我们的分级是自洽的

    (第三级方案一定包含于第二级方案)

    我们发现第二级方案只存在n种,(固定最短或最长区间后方案唯一)

    如何枚举这n种呢;

    滑动窗口枚举即可!!!!!!!!!!!!!!!!!!!!!!!!!!

    (当我们按顺序枚举某方案的最长区间时,与她对应的最短区间的位置单调不降)

    然后是代码:

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 int t_max[3000000],t_lz[3000000];
     5 struct ss{
     6     int p[2],w;
     7 }x[500010];
     8 int n,m;
     9 int b[1000010];
    10 bool cmp1(int i,int j){
    11     return x[(i+1)>>1].p[(i&1)^1]<x[(j+1)>>1].p[(j&1)^1];
    12 }
    13 bool cmp2(ss i,ss j){
    14     return i.w>j.w;
    15 }
    16 void add(int,int,int,int,int,int);
    17 int main()
    18 {
    19     int i,j=0,k=1,ans=1000000002;
    20     scanf("%d%d",&n,&m);
    21     for(i=1;i<=n;i++){
    22         scanf("%d%d",&x[i].p[0],&x[i].p[1]);
    23         x[i].w=x[i].p[1]-x[i].p[0];
    24         b[++j]=(i<<1)-1;
    25         b[++j]=i<<1;
    26     }
    27     sort(b+1,b+j+1,cmp1);
    28     for(i=1;i<=j;i++){
    29         x[(b[i]+1)>>1].p[(b[i]&1)^1]=k;
    30         if(x[(b[i]+1)>>1].p[(b[i]&1)^1]!=x[(b[i+1]+1)>>1].p[(b[i+1]&1)^1]&&i!=j)
    31             k++;
    32     }
    33     sort(x+1,x+n+1,cmp2);
    34     j=0;
    35     for(i=1;i<=n;i++){
    36         add(1,k,1,x[i].p[0],x[i].p[1],1);
    37         while(t_max[1]>=m){
    38             if(ans>x[++j].w-x[i].w)
    39                 ans=x[j].w-x[i].w;
    40             add(1,k,1,x[j].p[0],x[j].p[1],-1);
    41         }
    42     }
    43     if(ans!=1000000002)
    44         printf("%d
    ",ans);
    45     else
    46         printf("-1");
    47     return 0;
    48 }
    49 void add(int l,int r,int nu,int L,int R,int X){
    50     if(L<=l&&r<=R){
    51         t_max[nu]+=X;
    52         t_lz[nu]+=X;
    53         return ;
    54     }
    55     if(t_lz[nu]){
    56         t_lz[nu<<1]+=t_lz[nu];t_lz[nu<<1|1]+=t_lz[nu];
    57         t_max[nu<<1]+=t_lz[nu];t_max[nu<<1|1]+=t_lz[nu];
    58         t_lz[nu]=0; 
    59     }
    60     int mid=(l+r)>>1;
    61     if(L<=mid)
    62         add(l,mid,nu<<1,L,R,X);
    63     if(R>=mid+1)
    64         add(mid+1,r,nu<<1|1,L,R,X);
    65     t_max[nu]=t_max[nu<<1]>t_max[nu<<1|1]?t_max[nu<<1]:t_max[nu<<1|1];
    66 }

    nlogn+n的离散,自认为写得不错;

    好像有写zkw的,但是我不会呀

    莫名其妙地就把题解写长了。。。

  • 相关阅读:
    【ZJOI 2008】 生日聚会
    BZOJ2135 刷题计划(贪心+二分)
    BZOJ2124 等差子序列(树状数组+哈希)
    BZOJ2282 SDOI2011消防/NOIP2007树网的核(二分答案+树形dp)
    BZOJ1304 CQOI2009叶子的染色(树形dp)
    BZOJ1283 序列(费用流)
    BZOJ1266 AHOI2006上学路线(最短路+最小割)
    BZOJ1041 HAOI2008圆上的整点(数论)
    BZOJ3505 CQOI2014数三角形(组合数学)
    BZOJ5206 JSOI2017原力(三元环计数)
  • 原文地址:https://www.cnblogs.com/nietzsche-oier/p/6528153.html
Copyright © 2011-2022 走看看