CF1555E Boring Segments
时间:2021-08-19 08:40:06
收藏:0
阅读:35
题解
第一眼想到了二分,然而因为最大最小值都不确定所以做不了。
因为两端都不确定,考虑先固定左端点(即最小值),不断将右端点右移,直到有解。
有解后再将左端点右移,此时有解时的右端点一定在原本的右端点处或右端点的右边(满足单调性)
这样右端点只会右移,在指针移动这一方面可以做到O(n)
貌似双指针是求最小极差的套路。
考虑如何快速判定一个权值区间是否有解。
先将序列排序,然后用线段树维护线段的增加与删除,这就是一个线段树维护区间覆盖的经典问题了。
具体而言,每个节点维护一个tot[i],表示完全覆盖该区间的线段数量
再维护一个cover[i],表示该区间是否被完全覆盖(有可能是tot[i]中的线段一条覆盖的,也可能是多条线段拼起来)
通过cover[i]=(tot[i]>0)||(cover[lc]&cover[rc])(lc,rc分别是左右儿子)维护。
注意当tot清零时要更新cover[i]=cover[lc]&cover[rc]
代码
#include<bits/stdc++.h>
using namespace std;
#define N 4000010
struct line
{
int l,r,w;
}lines[N];
bool operator <(line a,line b)
{
return a.w<b.w;
}
int tot[N];
bool cover[N];
#define lc id*2
#define rc id*2+1
#define mid (l+r)/2
void modify(int id,int l,int r,int tl,int tr,int val)
{
//cout<<id<<" "<<l<<" "<<r<<" "<<tl<<" "<<tr<<endl;
if(tl<=l&&r<=tr)
{
tot[id]+=val;
if(!tot[id]) cover[id]=cover[lc]&cover[rc];
else cover[id]=true;
//printf("[%d %d] %d\n",l,r,cover[id]);
return;
}
if(tl<=mid) modify(lc,l,mid,tl,tr,val);
if(tr>mid) modify(rc,mid+1,r,tl,tr,val);
cover[id]=(tot[id]>0)||(cover[lc]&cover[rc]);
//printf("[%d %d] %d\n",l,r,cover[id]);
}
signed main()
{
int n,m,cnt=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int l,r,w;
scanf("%d%d%d",&l,&r,&w);
r--;
if(r<l) continue;
if(r>m-1) r=m-1;
lines[++cnt]=(line){l,r,w};
}
sort(lines+1,lines+1+cnt);
int l=1,r=1,ans=1e9;
m--;
while(r<=cnt)
{
while(r<=cnt)
{
//printf("add [%d %d]\n",lines[r].l,lines[r].r);
modify(1,1,m,lines[r].l,lines[r].r,1);
if(cover[1]) break;
r++;
}
//printf("erase [%d %d]\n",lines[l].l,lines[l].r);
modify(1,1,m,lines[l].l,lines[l].r,-1);
if(r<=cnt) ans=min(ans,lines[r].w-lines[l].w);
else break;
l++;
if(r<l) r++;
}
cout<<ans;
}
原文:https://www.cnblogs.com/linzhuohang/p/15158935.html
评论(0)