【BZOJ4653】【NOI2016】区间 [线段树]
区间
Time Limit: 60 Sec Memory Limit: 256 MB[Submit][Status][Discuss]
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 为该区间的左右端点。
Output
只有一行,包含一个正整数,即最小花费。
Sample Input
6 3
3 5
1 2
3 4
2 2
1 5
1 4
3 5
1 2
3 4
2 2
1 5
1 4
Sample Output
2
HINT
N<=500000,M<=200000,0≤li≤ri≤10^9
Main idea
给定若干个区间,取出m个若这m个包含同一个位置则可以统计值,值为这m个中的最大区间长度减去最小区间长度,输出最小值。
Solution
发现答案为最大区间长度减去最小区间长度,显然可以先按照长度排序,然后枚举左端点(即最小区间长度),中间小于最大区间长度的区间的加入 只会使得更可能满足条件 而不会影响答案,所以我们用r表示当前加入到了右端点r这个位置,r显然递增,则可以用r一直往后推到满足条件了更新答案,用线段树维护区间+1。因为l[i]与r[i]长度较大,但是发现非常长的长度是不会影响是否可以统计的(因为满足统计的条件只要符合个数>=m即可),所以我们可以对l和r离散化。
Code
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cstdlib> 6 #include<cmath> 7 using namespace std; 8 9 const int ONE=1000001; 10 const int INF=2147483640; 11 12 int n,m; 13 int Ans,res; 14 int num,cnt; 15 16 struct lisan 17 { 18 int pos; 19 int value; 20 }Q[ONE*2]; 21 22 struct point 23 { 24 int l,r; 25 int value; 26 }a[ONE]; 27 28 struct power 29 { 30 int value; 31 int add; 32 }Node[ONE*4]; 33 34 int cmp(const point &a,const point &b) 35 { 36 return a.value<b.value; 37 } 38 39 int rule(const lisan &a,const lisan &b) 40 { 41 return a.value<b.value; 42 } 43 44 void pushdown(int i) 45 { 46 if(Node[i].add) 47 { 48 Node[i*2].add+=Node[i].add; 49 Node[i*2+1].add+=Node[i].add; 50 Node[i*2].value+=Node[i].add; 51 Node[i*2+1].value+=Node[i].add; 52 Node[i].add=0; 53 } 54 } 55 56 void Update(int i,int l,int r,int L,int R,int x) 57 { 58 if(L<=l && r<=R) 59 { 60 Node[i].add+=x; 61 Node[i].value+=x; 62 return; 63 } 64 pushdown(i); 65 66 int mid=(l+r)/2; 67 if(L<=mid) Update(i*2,l,mid,L,R,x); 68 if(mid+1<=R) Update(i*2+1,mid+1,r,L,R,x); 69 Node[i].value=max(Node[i*2].value,Node[i*2+1].value); 70 } 71 72 int get() 73 { 74 int res,Q=1; char c; 75 while( (c=getchar())<48 || c>57) 76 if(c=='-')Q=-1; 77 if(Q) res=c-48; 78 while((c=getchar())>=48 && c<=57) 79 res=res*10+c-48; 80 return res*Q; 81 } 82 83 void Getlisan() 84 { 85 for(int i=1;i<=n;i++) 86 { 87 Q[++num].value=a[i].l; Q[num].pos=num; 88 Q[++num].value=a[i].r; Q[num].pos=num; 89 } 90 91 sort(Q+1,Q+num+1,rule); 92 Q[0].value=-1; 93 for(int i=1;i<=num;i++) 94 { 95 if(Q[i].value!=Q[i-1].value) cnt++; 96 int x=(Q[i].pos+1)/2; 97 if(Q[i].pos % 2) a[x].l=cnt; 98 else a[x].r=cnt; 99 } 100 101 sort(a+1,a+n+1,cmp); 102 } 103 104 int main() 105 { 106 n=get(); m=get(); 107 for(int i=1;i<=n;i++) 108 { 109 a[i].l=get(); a[i].r=get(); 110 a[i].l++; a[i].r++; 111 a[i].value=a[i].r-a[i].l; 112 } 113 Getlisan(); 114 115 int r=0; 116 Ans=INF; 117 for(int i=1;i<=n;i++) 118 { 119 for(;;) 120 { 121 if(Node[1].value>=m || (r+1)>n) break; 122 r++; 123 Update(1,1,cnt,a[r].l,a[r].r,1); 124 } 125 if(Node[1].value>=m) 126 Ans=min(Ans,a[r].value-a[i].value); 127 128 Update(1,1,cnt,a[i].l,a[i].r,-1); 129 } 130 131 if(Ans==INF) printf("-1"); 132 else printf("%d",Ans); 133 }