[NOI2016]区间

Posted kakakakakaka

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2016]区间相关的知识,希望对你有一定的参考价值。

题目描述

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

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

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

输入输出格式

输入格式:

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

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

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

输出格式:

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

输入输出样例

输入样例#1: 复制
6 3
3 5
1 2
3 4
2 2
1 5
1 4
输出样例#1: 复制
2

题解:

好久以前做的一道题,补一下博客。

因为题目要求的是min(最大区间长度-最小区间长度),所以考虑按照区间长度排序。

尺取法,用线段树维护区间最大值,每次将相同长度的区间加入线段树,然后始终保持当前状态下有点被覆盖了m次。

在保持这个前提条件的情况下,尽量的出队,尽量的少入队,实际上就是保证了最大区间长度-最小区间长度最小。

坐标范围比较大,离散化一下就好。(这个离散化打得很丑)

 1 //Never forget why you start
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<cmath>
 7 #include<algorithm>
 8 #define ll(x) (x<<1)
 9 #define rr(x) (x<<1|1)
10 using namespace std;
11 int n,m;
12 struct node{
13   int l,r,len,ll,rr;
14 }a[500005];
15 bool cmp(const node a,const node b){
16   return a.len<b.len;
17 }
18 int sgm[4000005],lazy[4000005];
19 void push_up(int root,int left,int right){
20   sgm[root]=max(sgm[ll(root)],sgm[rr(root)]);
21 }
22 void build(int root,int left,int right){
23   if(left==right){
24     sgm[root]=0;
25     return;
26   }
27   if(left>right)return;
28   int mid=(left+right)>>1;
29   build(ll(root),left,mid);
30   build(rr(root),mid+1,right);
31   push_up(root,left,right);
32 }
33 void push_down(int root,int left,int right){
34   int mid=(left+right)>>1;
35   sgm[ll(root)]+=lazy[root];
36   sgm[rr(root)]+=lazy[root];
37   lazy[ll(root)]+=lazy[root];
38   lazy[rr(root)]+=lazy[root];
39   lazy[root]=0;
40 }
41 void insert(int root,int left,int right,int l,int r,int v){
42   if(l<=left&&right<=r){
43     sgm[root]+=v;
44     lazy[root]+=v;
45     return;
46   }
47   if(l>right||r<left)return;
48   push_down(root,left,right);
49   int mid=(left+right)>>1;
50   if(l<=mid)insert(ll(root),left,mid,l,r,v);
51   if(mid<r)insert(rr(root),mid+1,right,l,r,v);
52   push_up(root,left,right);
53 }
54 struct point{
55   int x,id,y;
56 }b[1000005];
57 bool cmp2(const point a,const point b){
58   return a.x<b.x;
59 }
60 bool cmp3(const point a,const point b){
61   return a.id<b.id;
62 }
63 int q[500005],ans=2000000000;
64 int main(){
65   int i,j;
66   scanf("%d%d",&n,&m);
67   for(i=1;i<=n;i++){
68     scanf("%d%d",&a[i].l,&a[i].r);
69     a[i].ll=a[i].l;a[i].rr=a[i].r;
70   }
71   for(i=1;i<=n;i++){
72     b[ll(i)].x=a[i].l;b[ll(i)].id=ll(i);
73     b[rr(i)].x=a[i].r;b[rr(i)].id=rr(i);
74   }
75   sort(b+2,b+(n<<1|1)+1,cmp2);
76   int end=(n<<1|1),cnt=0;
77   b[1].x=-1;
78   for(i=2;i<=end;i++){
79     if(b[i].x!=b[i-1].x)cnt++;
80     b[i].y=cnt;
81   }
82   sort(b+2,b+(n<<1|1)+1,cmp3);
83   for(i=1;i<=n;i++){
84     a[i].l=b[ll(i)].y;
85     a[i].r=b[rr(i)].y;
86     a[i].len=a[i].rr-a[i].ll;
87   }
88   sort(a+1,a+n+1,cmp);
89   build(1,1,cnt);
90   int l=1,r=0;
91   while(1){
92     while(sgm[1]<m&&r+1<=n)r++,insert(1,1,cnt,a[r].l,a[r].r,1);
93     while(sgm[1]>=m&&l<=r)ans=min(ans,a[r].len-a[l].len),insert(1,1,cnt,a[l].l,a[l].r,-1),l++;
94     if(r==n)break;
95   }
96   if(ans!=2000000000)printf("%d\n",ans);
97   else printf("-1\n");
98   return 0;
99 }

 

以上是关于[NOI2016]区间的主要内容,如果未能解决你的问题,请参考以下文章

[Noi2016]区间

AC日记——NOI2016区间 bzoj 4653

[BZOJ4653][Noi2016]区间

bzoj 4653: [Noi2016]区间

[NOI2016]区间

BZOJ4653: [Noi2016]区间