Boring Segments(线段树/双指针/sort)

题目
题意:给定n个线段,长度m,每个线段有有左右端点 l i , r i l_i,r_i li,ri和权重 w i w_i wi,现要从这n个线段选出若干个出来,使得选出的选段能衔接在一起,连通1到m。两个线段可以衔接,当且仅当它们有公共相交区域。对于每个能连通1到m的线段组合,取权值的最大值和最小值,现要求找出一组线段,使得能连通1到m,且最大值和最小值的差值最小化。
参考
题解:
看了题解才悟了按权值排序的重要性。一开始思考往线段的左右端点排序去了。
我们将线段按权值从小到大排序,接着利用双指针的思想,固定l,求能连通1到m的最小的r。我们可以往线段数里边添加边,维护最小值,当线段树的根节点的最小值大于0,则说明已完全覆盖了。

注意一个点,[1,2]和[3,5]这两个线段是不能衔接的,因为它们没有公共交点。即,我们不能当前把这个题目当成区间覆盖问题。解决方式是把每个线段的右节点都减一即可,这样区间[1,2]和[3,5](实际为[1,3],[3,6])就实际是有衔接的了,注意也要把m减一

线段树太久没写,懒惰标记都忘记加了_(:з」∠)_

#include<bits/stdc++.h> 
using namespace std;
const int maxn = 300010;
const int maxm = 1000010;
#define lson (rt << 1)
#define rson (rt << 1 | 1)

int mn[maxm<<2], lazy[maxm<<2];
int n, m;

struct node {
	int l, r;
	int w;
}a[maxn];
void print() {
	printf("a:\n");
	for (int i = 0; i < n; ++i) {
		printf("[%d, %d] %d\n", a[i].l, a[i].r, a[i].w);
	}
}
bool cmp(const node &a, const node &b) {
	return a.w < b.w;
}
void build() {
	memset(mn, 0, sizeof(mn));
	memset(lazy, 0, sizeof(lazy));
}
void pushup(int rt) {
	mn[rt] = min(mn[lson], mn[rson]);
}
void pushdown(int rt) {
	lazy[lson] += lazy[rt];
	lazy[rson] += lazy[rt];
	mn[lson] += lazy[rt];
	mn[rson] += lazy[rt];
	lazy[rt] = 0;
}
void update(int rt, int l, int r, int a, int b, int val) {
	if (l >= a && r <= b) {
		lazy[rt] += val;
		mn[rt] += val;
		return;
	}
	if (lazy[rt] != 0) pushdown(rt);
	int m = (l + r) >> 1;
	if (a <= m) update(lson, l, m, a, b, val);
	if (m < b) update(rson, m + 1, r, a, b, val);
	pushup(rt);
}
int main() {
	scanf("%d%d", &n, &m);
	--m;
	for (int i = 0; i < n; ++i) {
		scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].w);
		--a[i].r;
	}
	sort(a, a + n, cmp);
//	print();//
	build();
	int ans = maxm;
	int i = 0, j = -1;
	while (i < n) {
		while (j + 1 < n && mn[1] < 1) {
			++j;
			update(1, 1, m, a[j].l, a[j].r, 1);
		}
//		printf("[%d,%d] %d\n", i, j, mn[1]);
		if (mn[1] < 1) break;// attention for end case
		ans = min(ans, a[j].w - a[i].w);//[i,j]
		if (!ans) break;
		int pre = a[i].w;
		while (i <= j && a[i].w == pre) {
			update(1, 1, m, a[i].l, a[i].r, -1);
			++i;
		}
	}
	printf("%d\n", ans);
}
/*
9 10
6 7 10
2 3 16
5 6 13
7 8 20
9 10 6
4 5 4
3 4 10
1 2 6
8 9 20

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值