【bzoj5102】[POI2018]Prawnicy 堆
题目描述
定义一个区间(l,r)的长度为r-l,空区间的长度为0。
给定数轴上n个区间,请选择其中恰好k个区间,使得交集的长度最大。
输入
第一行包含两个正整数n,k(1<=k<=n<=1000000),表示区间的数量。
接下来n行,每行两个正整数l,r(1<=l<r<=10^9),依次表示每个区间。
输出
第一行输出一个整数,即最大长度。
第二行输出k个正整数,依次表示选择的是输入文件中的第几个区间。
若有多组最优解,输出任意一组。
样例输入
6 3
3 8
4 12
2 6
1 10
5 9
11 12
样例输出
4
1 2 4
题解
堆
考虑将所有区间按照左端点位置从小到大排序(套路),然后考虑答案中左端点最靠右的那个区间。
那么答案区间的左端点就是这个区间的左端点,右端点是选择的所有区间中的最小值。枚举左端点最靠右的区间,想让区间长度越大,就要让右端点越靠右。因此右端点是min(所有前面的区间中第k-1小的,当前区间右端点)。
由于k是固定的,因此可以在枚举过程中使用堆来维护这些取值,然后统计答案。
题目还要输出方案,因此还需要维护出答案的位置,再重新计算一遍即可。
时间复杂度 $O(n\log n)$
#include <queue> #include <cstdio> #include <cctype> #include <algorithm> using namespace std; struct data { int l , r , id; bool operator<(const data &a)const {return r > a.r;} }a[1000010]; priority_queue<data> q; bool cmp(data a , data b) { return a.l < b.l; } inline char nc() { static char buf[100000] , *p1 , *p2; return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ; } inline int read() { int ret = 0; char ch = nc(); while(!isdigit(ch)) ch = nc(); while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc(); return ret; } int main() { int n = read() , k = read() , i , ans = -1 << 30 , pos; for(i = 1 ; i <= n ; i ++ ) a[i].l = read() , a[i].r = read() , a[i].id = i; sort(a + 1 , a + n + 1 , cmp); for(i = 1 ; i < k ; i ++ ) q.push(a[i]); for(i = k ; i <= n ; i ++ ) { q.push(a[i]); if(q.top().r - a[i].l > ans) ans = q.top().r - a[i].l , pos = i; q.pop(); } printf("%d\n" , ans); while(!q.empty()) q.pop(); for(i = 1 ; i <= pos ; i ++ ) q.push(a[i]); for(i = k ; i < pos ; i ++ ) q.pop(); while(!q.empty()) printf("%d " , q.top().id) , q.pop(); return 0; }