1. 程式人生 > 其它 >題解 [NOI2016] 區間

題解 [NOI2016] 區間

題意

題目連結:傳送門

題面比較簡潔了。

碎碎念

開學前一天頹著頹著就看到這道題了

一開始想了一個假的做法:用一顆線段樹維護 \([L,R]\) 的最小花費和最大權值,大概寫了
\(30min\)之後發現沒辦法維護最大最小區間長........

然後 看了一眼題解看到了尺取法這三個字,又看了看標籤裡有排序,然後就沒有然後了———

Solution

首先注意到一個性質:增加一個區間不會使答案更優,去掉一個區間不會使答案變差

進而注意到對於一個區間的集合 \(S\),令\(min(S)=a,max(S)=b\),則長度在\([a,b]\)中的區間對答案沒有影響,也就是說它們是可有可無的。

於是我們得出一個結論,最優解的所有區間長度必然可以

連續

按區間長度升序將所有區間排序,然後雙指標掃掃一遍,右指標向前掃,不斷更新區間最大值,當這個最大值(也就是區間內一個點被覆蓋最多次數)大於等於\(m\)時,左指標向前走直到區間最大又小於\(m\)

顯然區間最值可以用線段樹維護,總的複雜度\(O(nlogn)\)


#include<bits/stdc++.h>
#define rep(a,b,c) for (register int a=b;a<=c;a++)
#define per(a,b,c) for (register int a=b;a>=c;a--)
using namespace std;
typedef long long ll;
template <typename T> inline void read(T &x){
	ll f = 1;x = 0;char ch = getchar();
	while (!isdigit(ch)){if (ch == '-')f = -1;ch = getchar();}
	while (isdigit(ch)){x = (x << 1)+(x << 3)+(ch ^ 48);ch = getchar();}
	x *= f;
}

const int MAXN=500100; 

int n,m;

int b[MAXN*2],cnt,tot;

struct Section{
	int x,y,len;
	bool operator < (const Section &X) const
	{
		return len==X.len?x<X.x:len<X.len;
	}
}a[MAXN];

struct Segtree
{
	struct node
	{
		int l,r,maxv,tag;
		#define l(i) tree[i].l
		#define r(i) tree[i].r
		#define maxv(i) tree[i].maxv
		#define tag(i) tree[i].tag
	}tree[MAXN*8];

	inline void build(int pos,int L,int R)
	{
		maxv(pos)=0;
		l(pos)=L,r(pos)=R;
		if(L==R) return;
		int mid=l(pos)+r(pos)>>1;
		build(pos<<1,L,mid);
		build(pos<<1|1,mid+1,R); 
	}
	
	inline void push_up(int pos)
	{
		maxv(pos)=max(maxv(pos<<1),maxv(pos<<1|1));
	}
	
	inline void push_down(int pos)
	{
		maxv(pos<<1)+=tag(pos),maxv(pos<<1|1)+=tag(pos);
		tag(pos<<1)+=tag(pos),tag(pos<<1|1)+=tag(pos);
		tag(pos)=0;
	}
	
	inline void modify(int pos,int L,int R,int val)
	{
		if(l(pos)>=L&&r(pos)<=R) 
		{
			maxv(pos)+=val;
			tag(pos)+=val;
			return;
		}
		if(l(pos)==r(pos))return;
		if(tag(pos))push_down(pos);
		int mid=l(pos)+r(pos)>>1;
		if(L<=mid)modify(pos<<1,L,R,val);		
		if(R>mid)modify(pos<<1|1,L,R,val);
		push_up(pos);
	} 
		
}T;

inline void init()
{
	scanf("%d%d",&n,&m);
	
	rep(i,1,n)
	{
		scanf("%d%d",&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;
	}
	sort(a+1,a+n+1);
	sort(b+1,b+tot+1);
	cnt=unique(b+1,b+tot+1)-b-1;
	 
}

inline void solve()
{
	T.build(1,1,cnt);
	int lpos,rpos;
	lpos=rpos=1;
	
	int ans=1e9; 
	while(lpos<=rpos&&rpos<=n)
	{
	
		int L=lower_bound(b+1,b+cnt+1,a[rpos].x)-b;
		int R=lower_bound(b+1,b+cnt+1,a[rpos].y)-b;	
		T.modify(1,L,R,1);
		
		while(T.maxv(1)>=m&&lpos<rpos)
		{	
			ans=min(ans,a[rpos].len-a[lpos].len);
			int tL=lower_bound(b+1,b+cnt+1,a[lpos].x)-b;
			int tR=lower_bound(b+1,b+cnt+1,a[lpos].y)-b;
			T.modify(1,tL,tR,-1);
			++lpos;
		}
		 
		++rpos;
	}
	if(ans!=1e9)printf("%d\n",ans);
	else printf("-1\n");
}

int main()
{
	init();
	solve();
	return 0;
}

  
`