NOIP2017P4 跳房子 题解

(题目描述略)

二分答案加动归,设 f i f_i fi 为到达第 i i i 格最大分数,则 f i = m a x ( f j ) + s i f_i = max(f_j) + s_i fi=max(fj)+si j j j 满足 j &lt; i j &lt; i j<i x i − d − g ≤ x j ≤ x i − d + g x_i - d - g ≤ x_j ≤ x_i - d + g xidgxjxid+g,为维护 m a x ( f j ) max(f_j) max(fj),考虑到随 i i i 增大,可选 f j f_j fj 区间向 j j j 增大方向移动,即每 f j f_j fj 至多进出可选区间一次,双堆维护或单调队列维护均可。其中双堆维护时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),会被卡常(读入优化后可能过);单调队列维护时间复杂度 O ( n ) O(n) O(n)

需要注意的是,中途某格可能无法到达的情况。在这种情况下,不能直接跳出循环(之后格可能到达),不能赋简单负值(之后可能被加成正值),不能赋极负值(被加负值后越界成正值),需特判不予维护。二分边界值为 m a x ( d − 1 , x n − d ) max(d - 1, x_n - d) max(d1,xnd) 需注意。

单调队列版代码如下:

#include<stdio.h>
#define INF (0x7FFFFFFF)
#define MAX_N (500005)
#define MaxInteger(a,b) \
({ \
	int __tmp_a=(a),__tmp_b=(b); \
	__tmp_a>__tmp_b?__tmp_a:__tmp_b; \
})
int f[MAX_N],fq[MAX_N],s[MAX_N],x[MAX_N];
int main()
{
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	int d,fl,fm,fr,g,gl,gm,gr,k,n;
	scanf("%d %d %d",&n,&d,&k);
	for(int i=1;i<=n;i++)
		scanf("%d %d",&x[i],&s[i]);
	x[0]=s[0]=0;
	g=gl=-1,gr=MaxInteger(d-1,x[n]-d);
	while(gl+1<gr)
	{
		f[0]=fl=fm=fr=0,fq[0]=fq[1]=2,gm=(gl+gr+1)/2;
		for(int i=1;i<=n;i++)
		{
			while(fr<i&&x[fr]<=x[i]-d+gm)
			{
				while(fq[0]<fq[1]&&fq[fq[1]-1]<f[fr])
					fq[1]--;
				fq[fq[1]++]=f[fr++];
			}
			while(fl<fr&&x[fl]<x[i]-d-gm)
				if(fq[fq[0]]==f[fl++])
					fq[0]++;
			f[i]=fq[0]<fq[1]&&fq[fq[0]]>-INF?fq[fq[0]]+s[i]:-INF;
			fm=MaxInteger(fm,f[i]);
		}
		if(fm<k)
			gl=gm;
		else
			g=gr=gm;
	}
	printf("%d",g);
	return 0;
}

双堆维护版代码如下:

#include<stdio.h>
#define INF (0x7FFFFFFF)
#define MAX_N (500005)
#define MaxInteger(a,b) \
({ \
	int __tmp_a=(a),__tmp_b=(b); \
	__tmp_a>__tmp_b?__tmp_a:__tmp_b; \
})
#define CmpMax(a,b) ((a)>(b))
#define HeapInsert(hep,key,cmp) \
{ \
	int __tmp_i,__tmp_k=(key); \
	for(__tmp_i=++hep[0];__tmp_i>1;__tmp_i/=2) \
		if(cmp(__tmp_k,hep[__tmp_i/2])) \
			hep[__tmp_i]=hep[__tmp_i/2]; \
		else \
			break; \
	hep[__tmp_i]=__tmp_k; \
}
#define HeapDelete(hep,cmp) \
{ \
	int __tmp_i,__tmp_j; \
	for(__tmp_i=1;__tmp_i*2<hep[0];__tmp_i=__tmp_j) \
	{ \
		__tmp_j=__tmp_i*2==hep[0]-1||cmp(hep[__tmp_i*2],hep[__tmp_i*2+1])?__tmp_i*2:__tmp_i*2+1; \
		if(cmp(hep[__tmp_j],hep[hep[0]])) \
			hep[__tmp_i]=hep[__tmp_j]; \
		else \
			break; \
	} \
	hep[__tmp_i]=hep[hep[0]--]; \
}
#define HeapTop(hep,hen,cmp) \
({ \
	while(hep[0]>0&&hen[0]>0&&hep[1]==hen[1]) \
	{ \
		HeapDelete(hep,cmp); \
		HeapDelete(hen,cmp); \
	} \
	hep[0]>0?hep[1]:0; \
})
int f[MAX_N],fh0[MAX_N],fh1[MAX_N],s[MAX_N],x[MAX_N];
#define Read \
({ \
	char __tmp_c;int __tmp_n=0,__tmp_s=1; \
	do __tmp_c=getchar(); \
	while((__tmp_c<'0'||__tmp_c>'9')&&__tmp_c!='-'); \
	if(__tmp_c=='-')__tmp_s=-1,__tmp_c=getchar(); \
	while(__tmp_c>='0'&&__tmp_c<='9') \
		__tmp_n=__tmp_n*10+(__tmp_c-'0'), \
		__tmp_c=getchar(); \
	__tmp_s==1?__tmp_n:-__tmp_n; \
})
int main()
{
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	int d,fl,fm,fr,g,gl,gm,gr,k,n;
	scanf("%d %d %d",&n,&d,&k);
	for(int i=1;i<=n;i++)
		x[i]=Read,s[i]=Read;
	x[0]=s[0]=0;
	g=gl=-1,gr=MaxInteger(d-1,x[n]-d);
	while(gl+1<gr)
	{
		f[0]=fh0[0]=fh1[0]=fl=fm=fr=0,gm=(gl+gr+1)/2;
		for(int i=1;i<=n;i++)
		{
			while(fr<i&&x[fr]<=x[i]-d+gm)
				HeapInsert(fh1,f[fr++],CmpMax);
			while(fl<fr&&x[fl]<x[i]-d-gm)
				HeapInsert(fh0,f[fl++],CmpMax);
			f[i]=HeapTop(fh1,fh0,CmpMax)+s[i];
			if(fh1[0]==0||fh1[1]==-INF)
				f[i]=-INF;
			fm=MaxInteger(fm,f[i]);
		}
		if(fm<k)
			gl=gm;
		else
			g=gr=gm;
	}
	printf("%d",g);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值