【BZOJ4653】【NOI2016】区间 线段树
题目大意
数轴上有\(n\)个闭区间\([l_1,r_1],[l_2,r_2],\ldots,[l_n,r_n]\),你要选出\(m\)个区间,使得存在一个\(x\),对于每个选出的区间\([l_i,r_i]\)都有\(x\in[l_i,r_i]\)。
一个方案的代价是最长区间长度减去最短区间长度。
求最小的代价。
无解输出\(-1\)。
\(m\leq n\leq 500000\)
题解
就是选出\(m\)个区间,包含同一个点。
如果多选一些区间,那么答案不会变小。
问题转化成选一些区间,使得存在一个点被\(m\)个区间包含。
把这些区间按长度排序,用线段树维护每个点被包含的次数。
枚举左端点和右端点,查询是否有一个点被包含\(m\)次。
设\(f_i\)为选第\(i\)个区间为最长的区间时最短的区间是哪个。
显然\(f_i\)是单调递增的
直接用两个指针维护就行了
因为区间端点的范围很大,所以要离散化。
时间复杂度:\(O(n\log n)\)
题解
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
	if(a>b)
		swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
	char str[100];
	sprintf(str,"%s.in",s);
	freopen(str,"r",stdin);
	sprintf(str,"%s.out",s);
	freopen(str,"w",stdout);
#endif
}
int rd()
{
	int s=0,c;
	while((c=getchar())<'0'||c>'9');
	do
	{
		s=s*10+c-'0';
	}
	while((c=getchar())>='0'&&c<='9');
	return s;
}
int upmin(int &a,int b)
{
	if(b<a)
	{
		a=b;
		return 1;
	}
	return 0;
}
int upmax(int &a,int b)
{
	if(b>a)
	{
		a=b;
		return 1;
	}
	return 0;
}
struct section
{
	int l,r;
	int d;
};
int cmp(section a,section b)
{
	return a.d<b.d;
}
namespace seg
{
	struct node
	{
		int l,r,ls,rs,s,t;
	};
	node a[2000010];
	int n;
	void build(int &p,int l,int r)
	{
		p=++n;
		a[p].l=l;
		a[p].r=r;
		if(l==r)
			return;
		int mid=(l+r)>>1;
		build(a[p].ls,l,mid);
		build(a[p].rs,mid+1,r);
	}
	void add(int p,int v)
	{
		a[p].s+=v;
		a[p].t+=v;
	}
	void push(int p)
	{
		if(a[p].l!=a[p].r&&a[p].t)
		{
			add(a[p].ls,a[p].t);
			add(a[p].rs,a[p].t);
			a[p].t=0;
		}
	}
	void add(int p,int l,int r,int v)
	{
		if(l<=a[p].l&&r>=a[p].r)
		{
			add(p,v);
			return;
		}
		int mid=(a[p].l+a[p].r)>>1;
		push(p);
		if(l<=mid)
			add(a[p].ls,l,r,v);
		if(r>mid)
			add(a[p].rs,l,r,v);
		a[p].s=max(a[a[p].ls].s,a[a[p].rs].s);
	}
	int query(int p,int l,int r)
	{
		if(l>r)
			return 0;
		if(l<=a[p].l&&r>=a[p].r)
			return a[p].s;
		push(p);
		int mid=(a[p].l+a[p].r)>>1;
		int res=0;
		if(l<=mid)
			upmax(res,query(a[p].ls,l,r));
		if(r>mid)
			upmax(res,query(a[p].rs,l,r));
		return res;
	}
}
using seg::build;
using seg::add;
using seg::query;
section a[500010];
int d[1000010];
int main()
{
//	open("bzoj4653");
	int n,m;
	scanf("%d%d",&n,&m);
	int i;
	int t=0;
	for(i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		a[i].d=a[i].r-a[i].l+1;
		d[++t]=a[i].l;
		d[++t]=a[i].r;
	}
	sort(d+1,d+t+1);
	t=unique(d+1,d+t+1)-d-1;
	sort(a+1,a+n+1,cmp);
	for(i=1;i<=n;i++)
	{
		a[i].l=lower_bound(d+1,d+t+1,a[i].l)-d;
		a[i].r=lower_bound(d+1,d+t+1,a[i].r)-d;
	}
	int j=1;
	int rt;
	build(rt,1,t);
	int ans=0x7fffffff;
	for(i=1;i<=n;i++)
	{
		add(rt,a[i].l,a[i].r,1);
		while(max(query(rt,a[j].l,a[j].r)-1,max(query(rt,1,a[j].l-1),query(rt,a[j].r+1,t)))>=m)
		{
			add(rt,a[j].l,a[j].r,-1);
			j++;
		}
		if(query(rt,1,t)>=m)
			ans=min(ans,a[i].d-a[j].d);
	}
	if(ans==0x7fffffff)
		ans=-1;
	printf("%d\n",ans);
	return 0;
}
												
											【BZOJ4653】【NOI2016】区间 线段树的更多相关文章
- BZOJ4653 [NOI2016]区间 [线段树,离散化]
		
题目传送门 区间 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就 ...
 - BZOJ4653: [Noi2016]区间(线段树 双指针)
		
题意 题目链接 Sol 按照dls的说法,一般这一类的题有两种思路,一种是枚举一个点\(M\),然后check它能否成为答案.但是对于此题来说好像不好搞 另一种思路是枚举最小的区间长度是多少,这样我们 ...
 - BZOJ4653:[NOI2016]区间(线段树)
		
Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x ...
 - 【BZOJ-4653】区间      线段树 + 排序 + 离散化
		
4653: [Noi2016]区间 Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 107 Solved: 70[Submit][Status][Di ...
 - BZOJ.4653.[NOI2016]区间(线段树)
		
BZOJ4653 UOJ222 考虑二分.那么我们可以按区间长度从小到大枚举每个区间,对每个区间可以得到一个可用区间长度范围. 我们要求是否存在一个点被这些区间覆盖至少\(m\)次.这可以用线段树区间 ...
 - [NOI2016]区间 线段树
		
[NOI2016]区间 LG传送门 考虑到这题的代价是最长边减最短边,可以先把边按长度排个序,双指针维护一个尺取的过程,如果存在包含某个点的区间数\(\ge m\),就更新答案并把左指针右移,这样做的 ...
 - Luogu P1712 [NOI2016]区间(线段树)
		
P1712 [NOI2016]区间 题意 题目描述 在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) .现在要从中选出 \(M\) 个区间, ...
 - UOJ222 NOI2016 区间 线段树+FIFO队列
		
首先将区间按长度排序后离散化端点(这里的“长度”指的是离散化之前区间的实际长度) 然后模拟一个队列,区间按排好的顺序依次进入,直到某个点被覆盖了M次.之后依次出队,直到所有点都被覆盖小于M次 修改和询 ...
 - 洛谷$P1712\ [NOI2016]$区间 线段树
		
正解:线段树 解题报告: 传送门$QwQ$ $umm$很久以前做的了来补个题解$QwQ$ 考虑给每个区间按权值($r-l$从大往小排序,依次加入,然后考虑如果有一个位置被覆盖次数等于$m$了就可以把权 ...
 - hdu 1540 Tunnel Warfare (区间线段树(模板))
		
http://acm.hdu.edu.cn/showproblem.php?pid=1540 Tunnel Warfare Time Limit: 4000/2000 MS (Java/Others) ...
 
随机推荐
- Centos7之Systemd(Service文件)详解
			
一.开机启动 对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system目录添加一个配置文件. 如果你想让该软件开机启动,就执行下面的命令(以httpd. ...
 - 关于对于system函数和c++标准下的新的变量定义方式{}
 - PS调出唯美冷色情侣婚纱写真照
			
一.打开PS原片,原片是一张JPG格式的片子 色温较高整个画面较红离对着上面的我们标准的韩式色调我们来进行调节吧 ,我就不打太多文字解释一些基本常规了 二.韩式婚纱内景喜欢加点烟雾.其实我本人是不太喜 ...
 - UITableView 的横向滑动实现
			
UITableView 的横向滑动实现 概述 为了实现横向滑动的控件,可以继承类 UIScrollView 或类 UIView 自定义可以横向滑动的控件,这里通过 UITableView 的旋转,实现 ...
 - 物联网框架ServerSuperIO
			
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
 - python_超级基础
			
初识计算机 CPU 计算机的大脑.中央处理单元,主要负责数据运算及计算,是运算计算中心. 存储器 内存 临时存储数据,供CPU运算使用. 优点: 读取速度快. 缺点: 容量小,成本高,断电即消失. 硬 ...
 - [2017BUAA软工助教]团队开发阶段CheckList
			
alpha阶段流程与相关节点 以下流程与团队项目中个人的得分点是一一对应的,详见QA文档中"个人在团队项目的得分部分" http://www.cnblogs.com/Childis ...
 - StackWalk64
			
#include <Windows.h> #define PULONG_PTR ULONG** #define PULONG ULONG* #define ULONG_PTR U ...
 - PHP单元测试PHPUnit
			
配置说明 1.全局安装phpunit命令脚本 1 2 3 4 5 $ wget https://phar.phpunit.de/phpunit-7.0.phar $ chmod +x phpunit- ...
 - Java案例-用户注册邮箱绑定激活功能实现
			
<–start–> 需求描述:当客户打开收到邮箱激活码的邮件,点击激活链接,正确填写激活码后就会完成邮箱激活的步骤. 在后台编程代码编写中,有以下几个要点: ① 接收客户的手机号码和邮箱激 ...