P3957 [NOIP2017 普及組] 跳房子

rgw2010發表於2024-07-31

思路:

首先發現單調性,靈活性增加 \(x+1\) 的答案肯定不會比增加 \(x\) 的答案更劣。

那麼可以二分求 \(g\),則機器人每次可以移動 \([\max(d-mid,1),d+mid]\) 這個區間內的距離,為了方便,設為 \([l,r]\)

考慮動態規劃求得能走到的最大分數,令 \(dp_i\) 表示走到第 \(i\) 個格子的最大分數,則狀態轉移方程為:

\[dp_i = s_i + \max\limits_{j=0}^{i-1} [x_i - r \le x_j] [x_j \le x_i - l] dp_j \]

可以使用單調佇列維護:

  • 若當前隊尾為 \(j\),且 \(x_j < x_i - r\),則這個 \(j\) 無法對 \(dp_i\) 造成貢獻,直接不斷彈出即可。

  • 若當前隊頭為 \(j\),且 \(x_{j+1} \le x_i - l\),則這個 \(j+1\) 可以對 \(dp_i\) 造成貢獻,不斷插入即可。

  • 因為要維護最大值,可以使用單調佇列。

時間複雜度為 \(O(N \log W)\)

完整程式碼:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=5e5+10;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
ll n,d,k,l,r,head,tail,ans=-1;
ll a[N],b[N],dp[N],Q[N];
ll check(ll l,ll r){
	head=1,tail=0;
	ll sum=0,x=1,y=0;
	for(int i=1;i<=n+1;i++){
		if(i>1)
		  dp[i]=-1e18;
		while(a[y+1]<=a[i]-l&&y<=n){
			y++;
			while(dp[y]>dp[Q[tail]]&&tail>=head)
			  tail--;
			if(dp[y]>=-1e18)
			  Q[++tail]=y;
		}
		while(a[x]<a[i]-r&&x<=n){
			if(Q[head]==x)
			  head++;
			x++;
		}
		if(tail>=head)
		  dp[i]=b[i]+dp[Q[head]];
		sum=max(sum,dp[i]);
	}
	return sum;
}
bool End;
int main(){
//	open("A.in","A.out");
	n=read(),d=read(),k=read();
	for(int i=1;i<=n;i++){
		a[i+1]=read();
		b[i+1]=read();
	}
	l=0,r=1e9;
	while(l<=r){
		ll mid=(l+r)>>1;
		if(check(max(d-mid,1ll),d+mid)>=k){
			r=mid-1;
			ans=mid;
		}
		else
		  l=mid+1;
	}
	write(ans);
	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}

相關文章