给定一个[1,m]
区间的坐标轴n条线段,每个覆盖[li,ri]
的区间并有一个价值w[i]
,我们可以从点u 到达点v 当且仅当u v
在同一个线段中,现在要选取一个代价最小的线段集合,使得我们可以从1到达m。 线段长度大于1。
代价指:所选取线段集合中w[i]最大值与最小值之差。
保证一定有解。
线段覆盖区间的问题,我们可以让右端点-1,然后区间加1,那么最终区间不为0的连续段,就可以任意跳了。 比如 [1,7] [8,9] 这两个线段是不相交的,右端点-1,变成[1,6] [8,8] ,做完区间加法之后,可以明显看到7这个位置是0。
于是得出,我们把n条线段的右端点各自-1,然后进行区间加,最终[1,m-1]这个区间不为0,就说明可以从1跳跃到m-1.
题目保证了一定有解,那么我们如何去找到一组max-min
最小的解呢?
像这种涉及两个变量的答案,我们可以采取双指针,先固定一个变量,寻找最优解,然后改变它,并继承改变前的信息。使得最终每个指针都是移动O(n)次。
我们把线段按代价排序。
枚举max,min
我们贪心地认为,只要能使得[1,m-1]最小值大于0,那么min越大越好。
于是:
- 如果当前的max无法满足题意,那么只好max指针右移。
- 否则,如果min指针对应的区间最小值大于等于2,意味着min指的这个线段删掉不破坏平衡,删掉可以使得最小值更大。所以区间更新-1,min右移。
具体流程:
m-=1,每个线段右端点-1作为预处理
- 枚举l , r分别指向当前选的线段中代价最小和最大的
- 进循环,每次先让r的对应区间+1,
- 检查l对应的区间最小值是否大于等于2,是的话就可以删掉当前l指的线段 ,并l++。重复这个过程。
- 检查【1,m】区间最小值是否大于0,是的话更新ans
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 1e6+10;
const int mx = 40;
const int mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
struct node
{
int l,r,w;
bool operator < (const node &f)const
{
return w < f.w;
}
}seg[maxn];//线段
int n,m;
int tree[maxn<<2],tag[maxn<<2];
inline int lc(int rt)
{
return rt<<1;
}
inline int rc(int rt)
{
return rt<<1|1;
}
inline void pushup(int rt)
{
tree[rt]=min(tree[lc(rt)],tree[rc(rt)]);
}
inline void change(int rt,int d)
{
tree[rt]+=d;
tag[rt]+=d;
}
inline void pushdown(int rt)
{
if(tag[rt])
{
change(lc(rt),tag[rt]);
change(rc(rt),tag[rt]);
tag[rt]=0;
}
}
inline void updata(int rt,int l,int r,int vl,int vr,int v)
{
if(vl> r || vr <l) return ;
if(vl<=l && r<=vr)
{
change(rt,v);
return ;
}
int mid = l+r>>1;
pushdown(rt);
updata(lc(rt),l,mid,vl,vr,v);
updata(rc(rt),mid+1,r,vl,vr,v);
pushup(rt);
}
inline int query(int rt,int l,int r,int vl,int vr)
{
if(vl> r || vr <l) return INF;
if(vl<=l && r<=vr)
{
return tree[rt];
}
int mid=l+r>>1;
pushdown(rt);
if(vr<=mid) return query(lc(rt),l,mid,vl,vr);
if(vl>mid) return query(rc(rt),mid+1,r,vl,vr);
return min(query(lc(rt),l,mid,vl,vr),query(rc(rt),mid+1,r,vl,vr));
}
int main()
{
scanf("%d %d",&n,&m);
for(re int i=1;i<=n;i++)
{
scanf("%d %d %d",&seg[i].l,&seg[i].r,&seg[i].w);
seg[i].r--;
}
m--;
sort(seg+1,seg+1+n);
int l=0,r=1;
int ans=INF;
while(l<=n && r<=n)
{
updata(1,1,m,seg[r].l,seg[r].r,1);
while(l<=r && query(1,1,m,seg[l].l,seg[l].r) >= 2)
{
updata(1,1,m,seg[l].l,seg[l].r,-1);//删掉不影响
l++;
}
if(query(1,1,m,1,m)>0) ans=min(ans,seg[r].w-seg[l].w);
r++;
}
cout<<ans<<'\n';
return 0;
}