(线段树 + 尺取法)[NOI2016] 区间

题目链接:

[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
输出描述:
只有一行,包含一个正整数,即最小花费。
输入

6 3
3 5
1 2
3 4
2 2
1 5
1 4

输出

2

思路:

我认为这道题的难点就是如何找到当一个x满足条件时,如何去算出覆盖在x上最长区间与最短区间之差,这个可以将每段区间按照长度
排序,(从大到小,从小到大都可以),我用的是从大到小,再用尺取法当满足条件时,对答案进行一次更新,最后的答案就是我们所求。

AcCode:

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 5e5 + 10, M = 2e5 + 10;
int n, m;
struct Segment{
	int l, r;
	int len;
}seg[N];
struct Node{
	int l, r;
	int cnt;
	int add;
}tr[N << 3];
vector<int> v;

bool cmp(Segment &x, Segment &y){return x.len > y.len;}
void pushup(int u){
	tr[u].cnt = max(tr[u << 1].cnt, tr[u << 1 | 1].cnt);
}
void pushdown(int u){
    if(tr[u].add){
        tr[u<<1].cnt += tr[u].add;
        tr[u<<1|1].cnt += tr[u].add;
        tr[u<<1].add += tr[u].add;
        tr[u<<1|1].add += tr[u].add;
        tr[u].add = 0;
    }
}
int find(int y){
	return lower_bound(v.begin(), v.end(), y) - v.begin() + 1;
}
void build(int u, int l, int r){
	tr[u].l = l, tr[u].r = r;
	if(l == r) return ;
	int mid = l + r >> 1;
	build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int l, int r, int k){
	if(tr[u].l >= l && tr[u].r <= r){
		//cout << tr[u].l << " " << tr[u].r << endl;
		tr[u].cnt += k;
		tr[u].add += k;
	}
	else{
		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if(l <= mid) modify(u << 1, l, r, k);
		if(r > mid) modify(u << 1 | 1, l, r, k);
		pushup(u);
	}
}
int eval(int i, int j){return seg[i].r - seg[i].l - seg[j].r + seg[j].l;}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> seg[i].l >> seg[i].r;
		seg[i].len = seg[i].r - seg[i].l;
		v.push_back(seg[i].l), v.push_back(seg[i].r);
	}
	sort(v.begin(), v.end());
	sort(seg + 1, seg + n + 1, cmp);
	v.erase(unique(v.begin(), v.end()), v.end());
	build(1, 1, n * 2);
	int res = inf;
	// for(int i = 1; i <= n; i++)
	// 	cout << find(seg[i].l) << " " << find(seg[i].r) << endl;
	// cout << "--------------" << endl;
	for(int i = 1, j = 0; i <= n; i++){
		//cout << tr[1].cnt << endl;
		while(j < n && tr[1].cnt < m)
			j ++, modify(1, find(seg[j].l), find(seg[j].r), 1);
		if(j == n || tr[1].cnt < m) break;
		//cout << j << endl;
		modify(1, find(seg[i].l), find(seg[i].r), -1);
		res = min(res, eval(i, j));
	}
	//cout << tr[1].cnt << endl;
	if(res == inf) cout << -1 << endl;
	else cout << res << endl;
	return 0;
}
posted @   ReSakura  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示