尺取法

今天学了个高级东西——尺取法。(这句话在很多大佬看来是个笑话)。

以下摘自《挑战程序设计竞赛(第二版)》

尺取法通常指对数组保存一对下标(起点和终点),然后根据实际情况交替推进两个端点直到得到答案的方法。这种操作很像是尺蠖(日文中称为尺取虫)爬行的方式故得名。

举几个栗子:
POJ-3061
题意是给你一个正整数数列,一个整数 \(S\),问和不小于 \(S\) 的连续子序列的最小长度,无解输出 \(0\)
尺取法入门题。
题目保证 \(a_i > 0\),所以右端点具有单调性,故可以用尺取法。
按照尺取法,设定前后指针 \(s=0\)\(t=0\),然后将 \(t\) 向前移动,判断 \(s\) ~ \(t\) 之间的区间是否满足要求,如果小于 \(S\)\(t\) 继续移动,直到和大于等于 \(S\) 为止。此时将 \(t\) 向前移动。反复进行该过程,期间记录长度,并更新长度的最小值。复杂度为 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 1e5+5, inf = 1<<30;

int a[N];

inline void work()
{
	int l,r,i,n,lim,s,ans;
	n=gi(), lim=gi();
	for (i=1; i<=n; ++i)
		a[i]=gi();
	l=r=1, s=a[l], ans=inf;
	while (r <= n)
		{
			while (s < lim && r <= n)
				s+=a[++r];
			if (s < lim)
				break;
			ans=min(ans,r-l+1), s-=a[l++];
		}
	ans < inf ? printf("%d\n",ans) :puts("0");
}

int main()
{
	// freopen("sub.in","r",stdin);
	// freopen("sub.out","w",stdout);
	int t;
	t=gi();
	while (t--)
		work();
	return 0;
}

NOI2016 【区间】
题目不解释了。
首先,喻队长教育我:

“他坐标范围这么大,肯定要离散化,所以交点一定可以在端点上。”

傻逼的我想了30秒,发现好有道理:

判断是否有交是左端点取 \(max\),右端点取 \(min\)

于是只需要每次判断是否有一个区间的一个端点被覆盖 \(m\) 次即可。
剩下的部分就是贪心了。
把区间按长度排序即可。然后用尺取法,每次用线段树维护一下所有点被覆盖次数的 \(max\) 即可。
至于为什么可以一直加,是因为这个区间就算不在 \(h\) 的要选的 \(m\) 个区间内,它选了也不会对答案造成影响,因为后面肯定还有必须要选的区间 \(t\),即当前点要选的第 \(m\) 个区间。(这个有点像最大子序列和的思想)

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 1e6+5, inf = (1ll<<31)-10;

struct ran
{
	int l,r,len;
	inline bool operator <(const ran &R) const { return len < R.len; }
}r[N];
int b[N<<1],tr[N<<2],lz[N<<2];

inline void down(RG int o)
{
	if (!lz[o])
		return;
	RG int ls,rs;
	ls=o<<1, rs=ls|1;
	lz[ls]+=lz[o], lz[rs]+=lz[o];
	tr[ls]+=lz[o], tr[rs]+=lz[o];
	lz[o]=0;
}

inline void update(RG int o,RG int l,RG int r,RG int s,RG int t,RG int w)
{
	if (l >= s && r <= t)
		{
			tr[o]+=w, lz[o]+=w;
			return;
		}
	down(o);
	RG int mid,ls,rs;
	mid=(l+r)>>1, ls=o<<1, rs=ls|1;
	if (s <= mid)
		update(ls,l,mid,s,t,w);
	if (t > mid)
		update(rs,mid+1,r,s,t,w);
	tr[o]=max(tr[ls],tr[rs]);
}

int main()
{
	// freopen("ran.in","r",stdin);
	// freopen("ran.out","w",stdout);
	int n,m;
	RG int h,t,cnt,i,ans;
	n=gi(), m=gi();
	cnt=0;
	for (i=1; i<=n; ++i)
		{
			r[i].l=gi(), r[i].r=gi(), r[i].len=r[i].r-r[i].l;
			b[++cnt]=r[i].l, b[++cnt]=r[i].r;
		}
	sort(r+1,r+n+1);
	sort(b+1,b+cnt+1);
	cnt=unique(b+1,b+cnt+1)-b-1;
	for (i=1; i<=n; ++i)
		{
			r[i].l=lower_bound(b+1,b+cnt+1,r[i].l)-b;
			r[i].r=lower_bound(b+1,b+cnt+1,r[i].r)-b;
		}
	h=t=1, ans=inf;
	while (true)
		{
			while (tr[1] < m && t <= n)
				update(1,1,cnt,r[t].l,r[t].r,1), t++;
			if (tr[1] < m)
				break;
			ans=min(ans,r[t-1].len-r[h].len);
			update(1,1,cnt,r[h].l,r[h].r,-1), h++;
		}
	ans < inf ? printf("%d\n",ans) : puts("-1");
	return 0;
}
posted @ 2017-09-24 20:17  tbhkoymiads  阅读(422)  评论(0编辑  收藏  举报