P1442 铁球落地

铁球落地

题目描述

在二维坐标系内有 n n n 个平台(定义平台是一条两端点纵坐标相同的开线段,开线段指线段两个端点不算做线段本身)和一个铁球,铁球如果下面没有物体,则每秒会下落一个单位长度。

球每次落到某个平台上后,游戏者可以选择水平向左或水平向右滚,球滚动速度是每秒 1 1 1 个单位长度。由于铁球的质量不太好,每次落下的高度不能超过 h h h

设计一种策略,使得球尽快落到地面而不被摔碎。

假设地面高度为 0 0 0,且无限宽。球体积相对平台极小,可以看作一个质点。请注意,球滚动至平台的一个端点处即可下落,不需要滚动至下一个格子。例如下图,小球在 ( 9 , 9 ) (9,9) (9,9) 处已经开始下落。

qwq

样例 #1

样例输入 #1

5 3
6 10
5 2 4
9 3 9
6 7 10
2 1 5
3 8 11

样例输出 #1

15

提示

数据规模与约定

对于全部的测试点,保证:

  • 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105
  • 1 ≤ x , y , h , h i , l i , r i ≤ 1 0 9 1 \leq x, y, h, h_i, l_i, r_i \leq 10^9 1x,y,h,hi,li,ri109 l i ≤ r i l_i \leq r_i liri
  • 对于所有的 h i h_i hi,保证互不相同, l i l_i li r i r_i ri 也互不相同,且对于任意 i ≠ j i \neq j i=j,保证 l i ≠ r j l_i \neq r_j li=rj
  • 数据保证有解,最终答案不超过 1 0 9 10^9 109



解析:

对于一个平台,球可以从左右两侧下落。令 f i , 0 / 1 f_{i,0/1} fi,0/1 表示球在左/右端点处到地面的最短时间

假设平台 i i i 左端点下面是平台 j j j ( j j j 不是地面),则 f i , 0 = m i n ( f i , 0 , f j , 0 + h i − h j + l i − l j ) f_{i,0} =min(f_{i,0}, f_{j,0}+h_i-h_j + l_i-l_j) fi,0=min(fi,0,fj,0+hihj+lilj) f i , 0 = m i n ( f i , 0 , f j , 1 + h i − h j + r j − r i ) f_{i,0} =min(f_{i,0}, f_{j,1}+h_i-h_j + r_j-r_i) fi,0=min(fi,0,fj,1+hihj+rjri)从平台 i i i 的右端点落下去同理。

可以预处理出每个平台左右端点下边是哪个平台。用线段树预处理,支持区间赋值和单点查询。

对所有平台按高度升序排序,在线段树上查询左右端点处的平台,然后将当前平台对应区间修改为当前平台。

状态转移时,从低往高转移。注意下,平台高度差大于 m m m 时不能转移;如果当前平台的下边平台是地面时, f i = h i f_i = h_i fi=hi,因为不需要再到下一个平台的端点。

注意要离散化。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 2e5+10;
const ll INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int, int> pii;

inline int ls(int x){return x << 1;}
inline int rs(int x){return x << 1|1;}

struct sgt{
	ll v, tag;
}t[maxn<<2];
void pushdown(int k){
	if(t[k].tag){
		t[ls(k)].tag = t[ls(k)].v = t[k].tag;
		t[rs(k)].tag = t[rs(k)].v = t[k].tag;
		t[k].tag = 0;
	}
}
void build(int k, int l, int r){
	if(l == r){
		t[k].tag = t[k].v = 0;
		return;
	}
	int mid = (l+r) >> 1;
	build(ls(k), l, mid);
	build(rs(k), mid+1, r);
}
void modify(int k, int l, int r, int x, int y, int v){
	if(x <= l && y >= r){
		t[k].tag = v;
		t[k].v = v;
		return;
	}
	pushdown(k);
	int mid = (l+r) >> 1;
	if(x <= mid)
		modify(ls(k), l, mid, x, y, v);
	if(y > mid)
		modify(rs(k), mid+1, r, x, y, v);
}
ll query(int k, int l, int r, int pos){
	if(l == r)
		return t[k].v;
	pushdown(k);
	int mid = (l+r) >> 1;
	ll res = 0;
	if(pos <= mid)
		res = query(ls(k), l, mid, pos);
	else if(pos > mid)
		res = query(rs(k), mid+1, r, pos);
	return res;	
}
struct node{
	int id, l, r, h;
	bool operator < (const node &b) const{
		return h < b.h;
	}
}a[maxn];

int xx, yy;
int n, h, tot;
ll dp[maxn][2];//左0右1 
ll x[maxn];
ll la[maxn], ra[maxn];
ll down[maxn][2];
void DP(){
	memset(dp, INF, sizeof(dp));
	dp[0][1] = dp[0][0] = 0;
	for(int i = 1; i <= n; i++){
		if(a[i].h - a[down[i][0]].h <= h){
			if(down[i][0]){
				dp[i][0]=min(dp[i][0], dp[down[i][0]][0]+la[a[i].id]-la[a[down[i][0]].id]+a[i].h-a[down[i][0]].h);
                dp[i][0]=min(dp[i][0], dp[down[i][0]][1]+ra[a[down[i][0]].id]-la[a[i].id]+a[i].h-a[down[i][0]].h);
			}
			else
				dp[i][0] = a[i].h;
		}
		if(a[i].h - a[down[i][1]].h <= h){
			if(down[i][1]){
				dp[i][1]=min(dp[i][1],dp[down[i][1]][0]+ra[a[i].id]-la[a[down[i][1]].id]+a[i].h-a[down[i][1]].h);
                dp[i][1]=min(dp[i][1],dp[down[i][1]][1]+ra[a[down[i][1]].id]-ra[a[i].id]+a[i].h-a[down[i][1]].h);
			}
			else
				dp[i][1] = a[i].h;
		}
	}
	cout << dp[n][0] << endl;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> h;
	cin >> xx >> yy;
	
	for(int i = 1; i <= n; i++){
		cin >> a[i].h >> a[i].l >> a[i].r;
		a[i].id = i;
		la[i] = a[i].l, ra[i] = a[i].r;
		x[++tot] = a[i].l;
		x[++tot] = a[i].r;
	}
	
	n++;
	a[n] = (node){n, xx, xx, yy};
	la[n] = xx, ra[n] = xx;	
	x[++tot] = xx;
	
	sort(x+1, x+tot+1);
	tot = unique(x+1, x+tot+1) - (x+1);
	sort(a+1, a+1+n);
	
	for(int i = 1; i <= n; i++){
		a[i].l = lower_bound(x+1, x+tot+1, a[i].l) - x;
		a[i].r = lower_bound(x+1, x+tot+1, a[i].r) - x;
	}
	
	for(int i = 1; i <= n; i++){
		down[i][0] = query(1, 1, tot, a[i].l);
		down[i][1] = query(1, 1, tot, a[i].r);		
		modify(1, 1, tot, a[i].l, a[i].r, i);
	}
	DP();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值