CF1555 E. Boring Segments(线段树+双指针)
题意:
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); }