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;
}
posted @ 2019-03-24 21:38  wxyww  阅读(162)  评论(0编辑  收藏  举报