铁球落地
题目描述
在二维坐标系内有 n n n 个平台(定义平台是一条两端点纵坐标相同的开线段,开线段指线段两个端点不算做线段本身)和一个铁球,铁球如果下面没有物体,则每秒会下落一个单位长度。
球每次落到某个平台上后,游戏者可以选择水平向左或水平向右滚,球滚动速度是每秒 1 1 1 个单位长度。由于铁球的质量不太好,每次落下的高度不能超过 h h h。
设计一种策略,使得球尽快落到地面而不被摔碎。
假设地面高度为 0 0 0,且无限宽。球体积相对平台极小,可以看作一个质点。请注意,球滚动至平台的一个端点处即可下落,不需要滚动至下一个格子。例如下图,小球在 ( 9 , 9 ) (9,9) (9,9) 处已经开始下落。
样例 #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 1≤n≤105。
- 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 1≤x,y,h,hi,li,ri≤109, l i ≤ r i l_i \leq r_i li≤ri。
- 对于所有的 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+hi−hj+li−lj) 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+hi−hj+rj−ri)从平台 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;
}