noi.ac89A 电梯

题目

思路

首先按照(t)排序!!!!

首先考虑一个暴力(dp)

(f[i])表示前(i)个人到达地点所需要的时间。

那么就有如下的转移

[f_i = min_{1 le j le i}(max(f_j,t_i) + max{w_{j + 1} ... w_i} * 2) ]

这样复杂度是(o(n^2))

考虑优化上面的(dp)

因为已经按照(t)从小到大排过序了,所以如果(w_i le w_{i + 1})那么第(i)这个人就一定是和第(i+1)个人一起跟优秀。所以就可以把第(i)人剔除掉。

用单调栈可以完成上面的工作。

现在就变成了一个(t)单调递增,(w)单调递减的序列

然后再去看上面的(dp),我们可以把它分成两段。
(if(f_j le t_i))

[f_i=t_i + max{w_{j+1}...w_i} * 2 ]

(if(f_j > t_i))

[f_i = f_j + max{w_{j+1}...w_i} * 2 ]

因为(w)数组单减,所以上面的式子(max{w_{j+1}...w_i})是肯定是(w_{j+1})

所以上面的(dp)式子可以这样写

(if(f_j le t_i))

[f_i=t_i + w_{j+1} * 2 ]

(if(f_j > t_i))

[f_i = f_j + w_{j+1} * 2 ]

因为(f)数组是递增的,所以第一种转移肯定一前一部分,第二种转移是后一部分。可以找到他们的分界点(p)

对于(p)(p)之前的,因为(t_i)固定了,所以只要找到最小的(w_{j + 1})就行了。显然(w_{p+1})最小

对于(p)之后的,就是要找最小的(f_j+w_{j+1}*2),用线段树维护一下。

代码

/*
* @Author: wxyww
* @Date:   2019-03-24 19:59:19
* @Last Modified time: 2019-03-24 20:43:41
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const ll INF = 4e9,N = 1000000 + 100;
#define int ll
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*10+c-'0';
		c=getchar();
	}
	return x*f;
}
int b[N],top,tree[N << 2], f[N];
struct node {
	int p,w;
}a[N];
void update(int rt,int l,int r,int pos,int c) {
	if(l == r) {
		tree[rt] = c;
		return;
	}
	int mid = (l + r) >> 1;
	if(pos <= mid) update(rt << 1,l,mid,pos,c);
	else update(rt << 1 | 1,mid + 1,r,pos,c);
	tree[rt] = min(tree[rt << 1],tree[rt << 1 | 1]);
}
int query(int rt,int l,int r,int L,int R) {
	if(L <= l && R >= r) return tree[rt];
	int mid = (l + r) >> 1;
	int ans = INF;
	if(L <= mid) ans = min(ans,query(rt << 1,l,mid,L,R));
	if(R > mid) ans = min(ans,query(rt << 1 | 1,mid + 1,r,L,R));
	return ans;
}
bool cmp(const node &A,const node &B) {
	return A.p < B.p;
}
signed main() {
	int n = read();
	for(int i = 1;i <= n;++i) a[i].p = read(),a[i].w = read();

	sort(a + 1,a + n + 1,cmp);

	for(int i = 1;i <= n;++i) {
		while(a[i].w >= a[b[top]].w && top) top--;
		b[++top] = i;
	}
	
	int p = 0;
	for(int i = 1;i <= top;++i) {
		while(p < i - 1 && f[p + 1] <= a[b[i]].p) ++p;
		f[i] = a[b[i]].p + a[b[p + 1]].w * 2;
		if(p + 1 <= i - 1) f[i] = min(f[i],query(1,1,top,p + 1,i - 1));
		update(1,1,top,i,f[i] + a[b[i + 1]].w * 2);
	}
	cout<<f[top];
	return 0;
}
原文地址:https://www.cnblogs.com/wxyww/p/10590615.html