P1712 [NOI2016]区间

P1712 [NOI2016]区间

题目描述

P1712 [NOI2016]区间

Solution

尺取法+线段树

一个显然的想法是按区间长度排序。

每一次多选取一个区间相当于区间覆盖次数加1,每一次少选取一个区间就有区间覆盖次数减1。

可以用线段树维护区间覆盖次数的最大值。

于是转化成了一个Two-Pointers的问题,尺取法扫一遍即可。

时间复杂度   O(nlogn)

Code

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=1e6+50;

inline int read()
{
    int x=0,f=1; char c=getchar();
    while (c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
    while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
    return x*f;
}
struct qnode{int opt,id,x,color; } q[MAXN];
int compareq(qnode x,qnode y) { return (x.x<y.x)||(x.x==y.x&&x.opt<y.opt); }
struct anode{int lpre,rpre,lnow,rnow,len; } a[MAXN];
int comparea(anode x,anode y){ return x.len<y.len; }
struct Segment_Tree
{
	struct segnode{int l,r,tag,mx; } tree[MAXN<<2];
	void up(int x){ tree[x].mx=max(tree[x<<1].mx,tree[x<<1|1].mx); }
	void down(int x)
	{
		if (tree[x].tag!=0)
		{
			int q=tree[x].tag;
			tree[x<<1].tag+=q,tree[x<<1].mx+=q;
			tree[x<<1|1].tag+=q,tree[x<<1|1].mx+=q;
			tree[x].tag=0;
		}
	}
	void build(int x,int l,int r)
	{
		if ((tree[x].l=l)==(tree[x].r=r)) return;
		int mid=(l+r)>>1;
		build(x<<1,l,mid);
		build(x<<1|1,mid+1,r);
	}
	int query(int x,int l,int r)
	{
		if (tree[x].l>=l&&tree[x].r<=r)  return tree[x].mx;
		down(x);
		int mid=(tree[x].l+tree[x].r)>>1;
		if (r<=mid) return query(x<<1,l,r);
		else if (l>mid) return query(x<<1|1,l,r);
		else return max(query(x<<1,l,mid),query(x<<1|1,mid+1,r));
	}
	void change(int x,int l,int r,int y)
	{
		if (tree[x].l>=l&&tree[x].r<=r)
		{
			tree[x].mx+=y;
			tree[x].tag+=y;
			return;
		}
		down(x);
		int mid=(tree[x].l+tree[x].r)>>1;
		if (r<=mid) change(x<<1,l,r,y);
		else if (l>mid) change(x<<1|1,l,r,y);
		else change(x<<1,l,mid,y),change(x<<1|1,mid+1,r,y);
		up(x);
	}	
} segment;
int main()
{
	int n=read(),m=read(),num=0;
	for (int i=1;i<=n;i++)
	{
		int l=read(),r=read();
		q[++num]=(qnode){0,i,l,0};
		q[++num]=(qnode){1,i,r,0};
	}
	sort(q+1,q+num+1,compareq);
	q[0].x=-1;
	for (int i=1;i<=num;i++) 
	{
	    q[i].color=q[i-1].color+(q[i].x!=q[i-1].x);
	    if (q[i].opt==0) a[q[i].id].lpre=q[i].x,a[q[i].id].lnow=q[i].color;
	    if (q[i].opt==1) a[q[i].id].rpre=q[i].x,a[q[i].id].rnow=q[i].color,a[q[i].id].len=a[q[i].id].rpre-a[q[i].id].lpre+1;
	}
	sort(a+1,a+n+1,comparea);
	cout<<endl;
	for (int i=1;i<=n;i++) cout<<a[i].lpre<<" "<<a[i].rpre<<" "<<a[i].lnow<<" "<<a[i].rnow<<" "<<a[i].len<<endl;
	segment.build(1,1,q[num].color);
	int ans=INF;
	for (int l=1,r=1;r<=n;r++)
	{
		segment.change(1,a[r].lnow,a[r].rnow,1);
		while (segment.query(1,1,q[num].color)>=m) 
		{
			ans=min(ans,a[r].len-a[l].len);
			segment.change(1,a[l].lnow,a[l].rnow,-1);
			l++;
		}
	}
	if (ans==INF) puts("-1"); 
	else printf("%d\n",ans);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值