[FROM LUOGU]跳房子

跳房子

传送门

被普及组爆踩Orz
这个题的DP方程很显然,每次二分出g值,带入验证
如果 m n < = x [ i ] − x [ j ] < = m x mn<=x[i]-x[j]<=mx mn<=x[i]x[j]<=mx,答案就可以进行转移:
f [ i ] = m a x ( f [ j ] + s [ i ] ) f[i]=max(f[j]+s[i]) f[i]=max(f[j]+s[i])
由于任意时刻均可以结束游戏,需要每次判断一下是否 f [ i ] > = k f[i]>=k f[i]>=k,成立就不用继续DP
然后可以单调队列优化

代码:
80 p t s ( 没 加 单 调 队 列 ) 80pts(没加单调队列) 80pts()

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
inline int rd(){
	int re data=0,w=1;static char ch=0;ch=getchar();
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data*w;
}
const int N=500005;
bool flag;int n,d,s[N],l=1,r,mid,x[N],ans=INT_MAX,q[N],head,tail;ll now,f[N],k;
inline bool check(int mn,int mx){
	memset(f,0xb3,sizeof(f)),now=f[0]=0,q[head=tail=1]=0;
	for(int re i=1;i<=n;++i){
		for(int re j=i-1;j>=0;--j){
			if(x[i]-x[j]<mn)continue;
			if(x[i]-x[j]>mx)break;
			f[i]=max(f[i],f[j]+s[i]);
		}now=max(f[i],now);
		if(now>=k)return 1;
	}
	return 0;
}
signed main(){
	n=rd(),d=rd(),k=rd();
	for(int re i=1;i<=n;++i)x[i]=rd(),s[i]=rd();r=1e9;
	if(!check(1,x[n]))puts("-1"),exit(0);
	while(l+1<r){
		mid=l+r>>1;
		if(check(max(d-mid,1),d+mid))ans=min(ans,mid),r=mid;
		else l=mid+1;
	}if(check(max(d-l,1),d+l))printf("%d",l);else if(check(max(d-mid,1),d+mid))printf("%d",mid);else printf("%d",r);
	exit(0);
}

100 p t s 100pts 100pts

#include<cstdio>
#include<iostream>
using namespace std;
#define re register
#define ll long long
#define inf 0x4f4f4f4f
inline char nc(){
    static char buf[10000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,10000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
	int re data=0,w=1;static char ch=0;ch=nc();
	while(!isdigit(ch)&&ch!='-')ch=nc();
	if(ch=='-')w=-1,ch=nc();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=nc();
	return data*w;
}
const int N=500005;
bool flag;int n,d,s[N],l=1,r,mid,x[N],ans=inf,q[N],head,tail;ll f[N],k;
inline bool check(int mn,int mx,int j=0){
	f[0]=0,q[head=1]=0,tail=0;
	for(int re i=1;i<=n;++i){
		while(x[i]-x[j]>=mn&&j<i){
			if(f[j]!=-inf){while(head<=tail&&f[q[tail]]<=f[j])--tail;q[++tail]=j;}
			++j;
		}
		while(head<=tail&&x[i]-x[q[head]]>mx)++head;
		if(head<=tail)f[i]=f[q[head]]+s[i];
		else f[i]=-inf;
		if(f[i]>=k)return 1;
	}
	return 0;
}
signed main(){
	n=rd(),d=rd(),k=rd();
	for(int re i=1;i<=n;++i)x[i]=rd(),s[i]=rd();r=x[n];
	if(!check(1,x[n])){puts("-1");return 0;}
	while(l<=r){
		mid=l+r>>1;
		if(check(max(d-mid,1),d+mid))ans=min(ans,mid),r=mid-1;
		else l=mid+1;
	}printf("%d",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值