【NOI2016】区间 线段树

题意

求有公共点的m个区间的最长区间的长度与最短区间长度的最小值。

题解

先将区间以长度为关键字从小到大进行排序,易得最小值应该是排序后尽可能相近的两个区间。用头尾两个指针控制,每次更新利用线段树进行区间覆盖来判断是否符合有公共点的条件。

代码

//bzoj4653[Noi2016]区间
#include
#include
#include
#include
#include
#define ll long long
#define lson l,mid,pos<<1
#define rson mid+1,r,pos<<1|1
#define N 500100
#define inf 0x7fffffffffffffff
using namespace std;
struct Tree{int lazy,mx,d;}tree[N<<4];
struct node{ll x,y,len;}a[N];
int n,m,ToT;ll b[N<<1],ans=inf;
bool cmp(node p,node q){return p.len<q.len;}
int find(int x,int l,int r)
{
	if (l==r) return l;
	int mid=(l+r)>>1;
	if (x<=b[mid]) return find(x,l,mid);
	else return find(x,mid+1,r);
}
void pushup(int x){tree[x].mx=max(tree[x<<1].mx,tree[x<<1|1].mx);}
void pushdown(int x)
{
	int cur=tree[x].lazy;
	tree[x<<1].mx+=cur;tree[x<<1].lazy+=cur;
	tree[x<<1|1].mx+=cur;tree[x<<1|1].lazy+=cur;
	tree[x].lazy=0;
}
void build(int l,int r,int pos)
{
	if (l==r) {tree[pos].mx=0;return;}
	int mid=(l+r)>>1;
	build(lson);build(rson);
	pushup(pos);
}
void update(int L,int R,int x,int l,int r,int pos)
{
	if (L<=l&&r<=R) {tree[pos].mx+=x;tree[pos].lazy+=x;return;}
	int mid=(l+r)>>1;
	pushdown(pos);
	if (L<=mid) update(L,R,x,lson);
	if (R>mid) update(L,R,x,rson);
	pushup(pos);
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&a[i].x,&a[i].y);
		b[++ToT]=a[i].x;b[++ToT]=a[i].y;
		a[i].len=a[i].y-a[i].x+1;
	}
	sort(a+1,a+n+1,cmp);sort(b+1,b+ToT+1);
	for (int i=1;i<=n;i++) a[i].x=find(a[i].x,1,ToT),a[i].y=find(a[i].y,1,ToT);
	//for (int i=1;i<=ToT;i++) printf("%d ",b[i]);printf("\n");
	//for (int i=1;i<=n;i++) printf("%d %d %d\n",a[i].x,a[i].y,a[i].len);
	build(1,ToT,1);
	for (int i=1,j=0;i<=n;i++)
	{
		while(tree[1].mx<m)
		{
			if (j==n) break;
			j++;
			update(a[j].x,a[j].y,1,1,ToT,1);
		}
		if (tree[1].mx>=m) ans=min(ans,a[j].len-a[i].len);
		else break;
		update(a[i].x,a[i].y,-1,1,ToT,1);
	}
	if (ans==inf) printf("-1\n");
	else printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(NOI,中级数据结构-线段树,noi,线段树)