刷题向》关于一道尺取法的神题目(BZOJ4653)(HARD-)(BZOJ 30题纪念)
不得不说,这也许会是一道长期在我的博客里作为“HARD”难度存在的题
这道题能很好的考验选手的思考能力,但本蒟蒻最后还是听了省队爷讲了之后才会。。。(默默面壁)
题目里,说对于每一个点,是用当前选出的M个里面,最长长度减去最短长度作为价值。也就是说:选择长度介于最长与最短之间的边,是对答案没有影响的。(本蒟蒻并没有想到这一点。。。)
所以由于这一点,我们可以先对于边的长度排序。
那么题目中提到的M,是“选中多余或等于M条边”,从这里就可以看出,我们只需要选定一个头和一个尾就好,由此可以看出,这个子问题完全可以用尺取法处理。
以尺取法不停更新最优解就好啦。
那么怎么处理M捏,其实很简单,用离散化加线段树就可以解决关于“M个点“的子问题。
直接甩题目&代码
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
3 5
1 2
3 4
2 2
1 5
1 4
Sample Output
2
1 /************************************************************** 2 Problem: 4653 3 User: PencilWang 4 Language: C++ 5 Result: Accepted 6 Time:12896 ms 7 Memory:87320 kb 8 ****************************************************************/ 9 10 #include<stdio.h> 11 #include<string.h> 12 #include<algorithm> 13 #include<set> 14 using namespace std; 15 struct shit1{int L,R,lon;}e[1010000]; 16 struct shit2{int L,R,lazy,num;}s[3030000]; 17 int fucker[1010000]; 18 int ans,n,m,k; 19 bool cmp(shit1 a,shit1 b) 20 { 21 return a.lon<b.lon; 22 } 23 void push_down(int p) 24 { 25 int L=p<<1,R=p<<1|1; 26 s[L].num+=s[p].lazy; 27 s[L].lazy+=s[p].lazy; 28 s[R].lazy+=s[p].lazy; 29 s[R].num+=s[p].lazy; 30 s[p].lazy=0; 31 return ; 32 } 33 void push_up(int p) 34 { 35 s[p].num=max(s[p<<1].num,s[p<<1|1].num); 36 return ; 37 } 38 void build(int p,int L,int R) 39 { 40 s[p].L=L,s[p].R=R; 41 if(L==R)return ; 42 int mid=(L+R)>>1; 43 build(p<<1,L,mid); 44 build(p<<1|1,mid+1,R); 45 return ; 46 } 47 void add(int a,int b,int p,int num) 48 { 49 if(a<=s[p].L&&s[p].R<=b) 50 { 51 s[p].num+=num; 52 s[p].lazy+=num; 53 return ; 54 } 55 push_down(p); 56 int mid=(s[p].L+s[p].R)>>1; 57 if(a<=mid)add(a,b,p<<1,num); 58 if(b>mid)add(a,b,p<<1|1,num); 59 push_up(p); 60 return ; 61 } 62 set<int>sb; 63 int main() 64 { 65 scanf("%d%d",&n,&k); 66 for(int i=1;i<=n;i++) 67 { 68 scanf("%d%d",&e[i].L,&e[i].R); 69 sb.insert(e[i].L);sb.insert(e[i].R); 70 e[i].lon=e[i].R-e[i].L+1; 71 } 72 for(set<int>::iterator p=sb.begin();p!=sb.end();++p) 73 fucker[++m]=*p; 74 build(1,1,m); 75 sort(e+1,e+n+1,cmp); 76 for(int i=1;i<=n;i++) 77 { 78 e[i].L=lower_bound(fucker+1,fucker+m+1,e[i].L)-fucker; 79 e[i].R=lower_bound(fucker+1,fucker+m+1,e[i].R)-fucker; 80 } 81 int L=1,R=1; 82 int Lz=e[1].lon,Rz=e[1].lon; 83 while(e[R].lon==Rz) 84 add(e[R].L,e[R].R,1,1),R++; 85 R--; 86 ans=0x3f3f3f3f; 87 while(L<=R&&R<=n) 88 { 89 if(s[1].num>=k) 90 { 91 ans=min(ans,Rz-Lz); 92 while(e[L].lon==Lz) 93 add(e[L].L,e[L].R,1,-1),L++; 94 Lz=e[L].lon; 95 } 96 else 97 { 98 Rz=e[++R].lon; 99 while(e[R].lon==Rz&&R<=n) 100 add(e[R].L,e[R].R,1,1),R++; 101 if(R==n+1)break; 102 R--; 103 } 104 } 105 if(ans==0x3f3f3f3f)ans=-1; 106 printf("%d",ans); 107 return 0; 108 }