Educational Codeforces Round 112 E. Boring Segments(双指针+线段树)

传送门

给定一个[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;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/121130664