【题解】LuoGu3957:跳房子

原题传送门
二分答案,然后 O ( n 2 ) O(n^2) O(n2)的dp来计算是否合理
这个暴力可以拿到50分

考虑优化dp部分,发现另外30分是 d = 1 d=1 d=1的情况,这样就有很多中思路,很容易想到单调队列,直接做出来了

满分做法,因为有一个区间,所以当前点 i i i的时候,与80分不同的是,不一定能从 i − 1 i-1 i1那个点转移过来,我们单调队列中要维护的是能跳到当前点的所有点
但是我又可以发现,随着枚举当前点 i i i,最后那个能跳到当前点的 l l l也在不断递增,所以就另外开一个变量 l l l,每次更新一下加入队尾就行了

Code:

#include 
#define maxn 500010
using namespace std;
int q[maxn], dp[maxn], n, d, k;
struct node{
	int x, s;
}a[maxn];

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

bool check(int mid){ //printf("            mid = %d\n", mid);
	int h = 1, t = 1, l = 1;
	q[1] = 0;
	for (int i = 1; i <= n; ++i) dp[i] = -1e9;
	for (int i = 1; i <= n; ++i){
		while (l < i && a[i].x - a[l].x > d + mid) ++l;
		while (l < i && a[i].x - a[l].x >= max(d - mid, 1)){
			while (h <= t && dp[l] >= dp[q[t]]) --t;
			q[++t] = l++;
		} 
		while (h <= t && a[i].x - a[q[h]].x > d + mid) ++h; //printf("l=%d;h=%d;t=%d;q[h]=%d;q[t]=%d;", l, h, t, q[h], q[t]);
		if (h <= t && dp[q[h]] != -1e9 && a[i].x - a[q[h]].x >= max(d - mid, 1) && a[i].x - a[q[h]].x <= d + mid) dp[i] = dp[q[h]] + a[i].s;// printf("dp[%d] = %d\n", i, dp[i]);
		if (dp[i] >= k) return 1;
	}
	return 0;
}

int main(){
	n = read(), d = read(), k = read();
	for (int i = 1; i <= n; ++i) a[i].x = read(), a[i].s = read();
	int l = 0, r = a[n].x, ans = -1;
	while (l <= r){
		int mid = (l + r) >> 1;
		if (check(mid)) ans = mid, r = mid - 1; else l = mid + 1;
	}
	printf("%d\n", ans);
	return 0;
}

你可能感兴趣的:(题解,noip,DP,题解,NOIp,LuoGu,单调队列,Dp)