题目
题目大意
给定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;
}