(线段树)(滑动窗口)【题解】「NOI2016」区间

这道题目的目标是找到一组区间,使得它们至少有一个公共点,同时求出所有合法方案中最小的花费。通过滑动窗口和线段树的数据结构,可以有效地判断条件并求解。题解中详细介绍了如何使用滑动窗口逐步筛选区间,并利用线段树进行计数和更新,以满足题目要求。最终,通过对区间进行离散化处理以适应大范围的数值,实现了能在限定时间内解决这个问题的算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题意

在数轴上有 n n n 个闭区间从 1 1 1 n n n 编号,第 i i i 个闭区间为 [ l i , r i ] [l_i,r_i] [li,ri]现在要从中选出 m m m 个区间,使得这 m m m 个区间共同包含至少一个位置。换句话说,就是使得存在一个 x x x ,使得对于每一个被选中的区间 [ l i , r i ] [l_i,r_i] [li,ri],都有 l i ≤ x ≤ r i l_i \leq x \leq r_i lixri对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。

区间$ [l_i,r_i]$的长度定义为 ( r i − l i ) (r_i-l_i) (rili),即等于它的右端点的值减去左端点的值。

求所有合法方案中最小的花费。如果不存在合法的方案,输出 − 1 -1 1

对于全部的测试点,保证 1 ≤ m ≤ n , 1 ≤ n ≤ 5 × 1 0 5 1 \leq m \leq n,1 \leq n \leq 5 \times 10^5 1mn1n5×105 1 ≤ m ≤ 2 × 1 0 5 1 \leq m \leq 2 \times 10^5 1m2×105 0 ≤ l i ≤ r i ≤ 1 0 9 0 \leq l_i \leq r_i \leq 10^9 0liri109

题解

1.前言

这道题在洛谷上的评级是紫,但我真心觉得没怎么难,可能因为我的方法并不是最优解吧

2.题解

滑动窗口求解:

先把所有区间一个一个的加,直到满足题目的要求

在依然满足要求的情况下,把最前面的区间一个一个的减去(这些区间是没有用的)

重复此过程,直到遍历完毕

注意其中我们如何快速的判断是否满足要求

我们不妨把题目的要求变一下形,要求一个点被至少 m m m条线段覆盖,即是被覆盖 m m m次,如果对于每一条线段,我们把覆盖操作想成是让这个区间内的每一个计数器都加1,知道整个区间内有一个点的计数器大于等于m

那么这就是线段树的板子了

3.代码

需要注意的地方有点多,比如l,r的范围较大,于是我们就要离散化

离散化需要开两倍的数组,所以线段树就要开8倍

时间复杂度 O ( 能 过 ) O(能过) O(),可以通过此题

#include<cstdio>
#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
const int MAXN=500005;
struct node{
	int l,r;
	long long val;
}a[MAXN];
void read(int &x){
	char c=getchar();
	int t=1;
	x=0;
	while(c<'0' || c>'9'){
		if(c=='-') t=-t;
		c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<1)+(x<<3)+(c-'0');
		c=getchar();
	}
	x*=t;
}
bool operator < (node a,node b){
	return a.val<b.val;
}
int b[2*MAXN];
int n,m;
int tl;
struct SegmentTree{
	int l,r,id;
	int maxn;
	int lazy;
}Tree[MAXN*8];
void pushup(int id){
	Tree[id].maxn=max(Tree[id<<1].maxn,Tree[id<<1|1].maxn);
}
void Spread(int id){
	Tree[id<<1].maxn+=Tree[id].lazy;
	Tree[id<<1].lazy+=Tree[id].lazy;
	Tree[id<<1|1].maxn+=Tree[id].lazy;
	Tree[id<<1|1].lazy+=Tree[id].lazy;
	Tree[id].lazy=0;
}
void build(int l,int r,int id){
	Tree[id].l=l;
	Tree[id].r=r;
	if(l==r) return;
	int mid=l+r>>1;
	build(l,mid,id<<1);
	build(mid+1,r,id<<1|1);
} 
void update(int x,int y,int v,int id){
	int l=Tree[id].l,r=Tree[id].r; 
	if(Tree[id].l>=x && Tree[id].r<=y){
		Tree[id].maxn+=v;
		Tree[id].lazy+=v;
		return;
	}
	Spread(id);
	int mid=(l+r)>>1;
	if(x<=mid){
		update(x,y,v,id<<1);
	}
	if(y>mid){
		update(x,y,v,id<<1|1);
	}
	pushup(id);
}
int main(){
	read(n);
	read(m);
	for(int i=1;i<=n;i++){
		read(a[i].l);
		read(a[i].r);
		a[i].val=a[i].r-a[i].l;
		b[i*2]=a[i].l,b[i*2+1]=a[i].r;
	}
	sort(b+1,b+n*2+2); 
	int M=unique(b+1,b+n*2+1)-(b+1);	
	int maxn=-1;
	for(int i=1;i<=n;i++){
		a[i].l=lower_bound(b+1,b+M+1,a[i].l)-b;
		a[i].r=lower_bound(b+1,b+M+1,a[i].r)-b;
		maxn=max(maxn,max(a[i].l,a[i].r));
		
	}
	sort(a+1,a+n+1);
	build(1,maxn,1);
	int l=0,r=0;
	long long ans=0x7f7f7f7f;
	while(r<n){
		while(Tree[1].maxn<m && r<=n){
			r++;
			update(a[r].l,a[r].r,1,1);
		}
		if(Tree[1].maxn<m) break;
		while(Tree[1].maxn>=m && l<=r){
			l++;
			update(a[l].l,a[l].r,-1,1);
		}
		ans=min(ans,(a[r].val-a[l].val));
	} 
	if(ans==0x7f7f7f7f){
		printf("-1");
		return 0;
	} 
	
	printf("%lld",ans);
return 0;
}
/*
5 1
50 30
10 20
30 40
20 10
40 50
*/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值