1. 程式人生 > >【bzoj5102】[POI2018]Prawnicy 堆

【bzoj5102】[POI2018]Prawnicy 堆

bool sdi main size stat 最大 type line 接下來

題目描述

定義一個區間(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;
}

【bzoj5102】[POI2018]Prawnicy 堆