【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;
}
posted @ 2018-03-06 11:28  ywwyww  阅读(162)  评论(0编辑  收藏  举报