CF1555 E. Boring Segments(线段树+双指针)

Problem - 1555E - Codeforces

题意:

n条线段,每条线段有权值,m个点,如果存在一条线段同时覆盖了点a和点b,那么就可以从点a到点b

选一些线段,要求可以通过选出的线段从点1到达点m

问选出的线段的最小差值

 

题意相当于问选出一些线段,这些线段覆盖了所有的点

我们先把所有的线段按权值从小到大排序

先求出最大权值最小的满足要求的线段权值,设为x

然后我们逐渐增大x,满足要求的权值区间下界会随着x的增大而增大

这就可以用双指针法,相当于固定了权值区间右端点求最小的权值区间左端点

左右端点都是单调向右移动的

如何判断当前选出的线段是否覆盖了所有的点?

用线段树维护,叶节点的点i表示i和i+1之间是否被覆盖了

线段树内维护区间覆盖次数的最小值即可

 

#include<bits/stdc++.h>

using namespace std;

#define N 300001
#define M 1000001

struct node
{
    int l,r,w;
}e[N];

int mi[M<<2],tag[M<<2];

bool cmp(node p,node q)
{
    return p.w<q.w;
}

void down(int k,int l,int mid,int r)
{
    tag[k<<1]+=tag[k];
    tag[k<<1|1]+=tag[k];
    mi[k<<1]+=tag[k];
    mi[k<<1|1]+=tag[k];
    tag[k]=0;
}

void add(int k,int l,int r,int opl,int opr,int w)
{
    if(l>=opl && r<=opr)
    {
        tag[k]+=w;
        mi[k]+=w;
        return;
    }
    int mid=l+r>>1;
    if(tag[k]) down(k,l,mid,r);
    if(opl<=mid) add(k<<1,l,mid,opl,opr,w);
    if(opr>mid) add(k<<1|1,mid+1,r,opl,opr,w);
    mi[k]=min(mi[k<<1],mi[k<<1|1]);
}

int query(int k,int l,int r,int opl,int opr)
{
    if(l>=opl && r<=opr) return mi[k];
    int mid=l+r>>1;
    if(tag[k]) down(k,l,mid,r);
    if(opr<=mid) return query(k<<1,l,mid,opl,opr);
    if(opl>mid) return query(k<<1|1,mid+1,r,opl,opr);
    return min(query(k<<1,l,mid,opl,opr),query(k<<1|1,mid+1,r,opl,opr)); 
}

int main()
{
    int n,m,ans=1e7;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].w);
    sort(e+1,e+n+1,cmp); 
    int r,l=1; 
    for(int i=1;i<=n;i=r)
    {
        r=i;
        while(r<=n && e[r].w==e[i].w) 
        {
            add(1,1,m-1,e[r].l,e[r].r-1,1);
            ++r;
        }
        while(l && query(1,1,m-1,e[l].l,e[l].r-1)>1) 
        {
            add(1,1,m-1,e[l].l,e[l].r-1,-1);
            ++l;
        }
        if(mi[1]) ans=min(ans,e[r-1].w-e[l].w);
    }
    printf("%d",ans);
}

 

posted @ 2021-09-23 11:58  TRTTG  阅读(46)  评论(0编辑  收藏  举报