單調隊列優化dp - WOJ#2735 跳房子

傳送門


Analysis

當年考NOIP普及組的時候,還不會dp
考場上看到這道題,直接果斷棄

時隔兩年,我終於能看懂題了/笑
顯然是一個二分+dp判斷合法

定義f[i]f[i]表示到達i這個位置可以獲得的最多分數
顯然f[i]=max(f[j])+score[i]f[i]=max(f[j])+score[i]
jj滿足max(1,dg)<=x[i]x[j]<=d+gmax(1,d-g)<=x[i]-x[j]<=d+g
如果只有後半部分的限制,直接單調隊列即可

考慮現在多了前半部分的限制
我們可以維護一個左指針,保證進入單調隊列裏的值都滿足前半部分的限制,後半部分的限制再由隊首彈掉

收穫:
單調隊列每次彈出隊列都只能滿足一個條件
另一個條件我們用其他方式維護
感覺這個方法很常用【sTO lsr神仙 Orz】

Code
#include<bits/stdc++.h>
#define in read()
#define re register
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<1)+(res<<3)+(ch^48);
		ch=getchar();
	}
	return f==1?res:-res;
}
const int N=5e5+10;
typedef long long ll;
int head,tail,q[N];
int n,d,K,x[N];
ll score[N],f[N];
bool check(int g){
	head=1;tail=0;f[0]=0;
	int up=d+g,down=max(1,d-g);
	int l=0;//傳說中的指針 
	for(re int i=1;i<=n;++i){
		while(x[i]-x[l]>=down){
			while(head<=tail&&f[l]>f[q[tail]]) tail--;
			q[++tail]=l;
			l++;
		}
		while(head<=tail&&x[i]-x[q[head]]>up) head++;
		if(head<=tail) f[i]=f[q[head]]+score[i];
		else f[i]=-1e18;//如果無法到達設爲-INF
	}
	ll ans=-1e18;
	for(re int i=0;i<=n;++i) ans=max(ans,f[i]);
	return ans>=K;
}
int main(){
	n=in;d=in;K=in;
	for(re int i=1;i<=n;++i) x[i]=in,score[i]=in;
	int l=0,r=1e9,ans=-1;
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	cout<<ans;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章