Educational Codeforces Round 112 (Rated for Div. 2)+E. Boring Segments

题目

题目传送门

题目大意

给定n个区间,表示l,l+1,…,r-1,r,相连,可以在这个区间内任意转移,现在要求一个区间子集,是得能从点1转移到点m,并且要求集合的中区间权值的最大值减去最小值最小。

解题思路

首先分析最大值减去最小值最小,可以联想到尺取法,将区间按照权值进行排序,利用贪心的思想,使得可行解的最大值减最小值最小,这是一个加区间删去区间的过程。
那么来思考区间算法,很容易联想到线段树。线段树需要维护什么?看是否每一个点都被联通,[1,4],[3,5]是连同的,[1,4],[5,6]是不连通的,如此思考,还必须判断两个区间是否有交集,这个线段树就变得十分复杂,区间的本意是联通,[1,3],1–2--3,[4,5]4–5,3和4之间没有被联通,那么我们可以将”—“左移,1表示1能连2,2表示2能连3,3不能联通4但可以到达3。那么这个问题就被转化为[1,m-1]是否都能联通后者。问题也被转化为,区间加边而不是区间加点,极大简化了线段树构建的难度,判断[1,m-1]是否都有边,就是查询区间的最小值.

code

#include<bits/stdc++.h>
#define il i<<1
#define ir (i<<1)+1
using namespace std;
const int maxm=1e6+6;
const int maxn=3e5+5;
struct edge
{
	int l,r,w;
	bool operator <(const struct edge &a)const
	{
		return w<a.w;
	} 
}e[maxn];
struct tree
{
	int l,r,mi,lz;
}t[4*maxm];
void build(int i,int l,int r)
{
	t[i].l=l;t[i].r=r;t[i].mi=0;t[i].lz=0;
	if(l==r)return ;
	int mid=(l+r)>>1;
	build(il,l,mid);
	build(ir,mid+1,r);
}
void update(int i)
{
	if(t[i].lz!=0)
	{
		t[il].mi+=t[i].lz;t[ir].mi+=t[i].lz;
		t[il].lz+=t[i].lz;t[ir].lz+=t[i].lz;
		t[i].lz=0;
	}
}
void add(int i,int l,int r,int w)
{
	if(t[i].l>=l&&t[i].r<=r)
	{
		t[i].lz+=w;
		t[i].mi+=w;return ;
	}
	update(i);
	if(l<=t[il].r)add(il,l,r,w);
	if(r>=t[ir].l)add(ir,l,r,w);
	t[i].mi=min(t[il].mi,t[ir].mi);
}
int main()
{
	ios::sync_with_stdio(false);
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){cin>>e[i].l>>e[i].r>>e[i].w;}
	sort(e+1,e+n+1);
	int res=1e9;
	build(1,1,m-1);int l=1;
	for(int i=1;i<=n;i++)
	{
		add(1,e[i].l,e[i].r-1,1); 
		while(t[1].mi>0)
		{
			res=min(res,e[i].w-e[l].w);
			add(1,e[l].l,e[l].r-1,-1);l++;
		}
	}
	cout<<res<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值