CF1555E Boring Segments


题解

第一眼想到了二分,然而因为最大最小值都不确定所以做不了。

因为两端都不确定,考虑先固定左端点(即最小值),不断将右端点右移,直到有解。

有解后再将左端点右移,此时有解时的右端点一定在原本的右端点处或右端点的右边(满足单调性)

这样右端点只会右移,在指针移动这一方面可以做到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
using namespace std;
#define N 4000010
struct line
{
	int l,r,w;
}lines[N];
bool operator <(line a,line b)
{
	return a.wmid) 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(rm-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