46 Fence Obstacle Course 题解
Fence Obstacle Course
题面
农夫约翰为他的奶牛们建造了一个围栏障碍训练场,以供奶牛们玩耍。
训练场由 N 个不同长度的围栏组成,每个围栏都与 x 轴平行,并且第 i 个围栏的 y 坐标为 i。
训练场的出口位于原点,起点位于 (S,N)
+-S-+-+-+ (fence #N)
+-+-+-+ (fence #N-1)
... ...
+-+-+-+ (fence #2)
+-+-+-+ (fence #1)
=|=|=|=*=|=|=| (barn)
-3-2-1 0 1 2 3
这些牛会从起点处开始向下走,当它们碰到围栏时会选择沿着围栏向左或向右走,走到围栏端点时继续往下走,按照此种走法一直走到出口为止。
请问,这些牛从开始到结束,行走的水平距离最少为多少。
\(1 \le N \le 10^5 ,\ -10^5 \le L_i \le 10^5\)
题解
考虑对于一条线段中间的点,一定需要走到左端点或者右端点才能继续向下走
但问题是向下走会走到哪条线段?这是这道题的难点
假如我们已经知道了从每条线段的左右端点向下走分别会走到哪条线段,那么我们就可以设 \(f(i,0/1)\) 表示从第 \(i\) 条线段走到终点的最小代价,那么就可以快速转移了
现在的问题是如何快速求出这个信息?
发现对于一条线段 \(i\) 来说,如果向下走碰到的不是第 \(i + 1\) 条线段,那么就一定是更靠下的线段,所以我们可以从下往上做一遍区间覆盖,然后每次查询左右端点被哪个区间包含即可
可以用线段树来维护,时间复杂度为 \(O(n \log n)\)
注意,题中的线段是从下向上给出的,所以可以直接做
code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
#define ls (p << 1)
#define rs (p << 1 | 1)
const int N = 2e5 + 10;
int n, S;
int L[N], R[N];
ll f[N][2];
struct node {
int l, r;
int tag, val;
} t[N << 2];
void build (int p, int l, int r) {
t[p].l = l, t[p].r = r;
t[p].val = t[p].tag = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build (ls, l, mid);
build (rs, mid + 1, r);
}
void pushdown (int p) {
if (!t[p].tag) return;
t[ls].tag = t[rs].tag = t[p].tag;
t[ls].val = t[rs].val = t[p].tag;
t[p].tag = 0;
}
void modify (int p, int l, int r, int c) {
if (l <= t[p].l && t[p].r <= r) {
t[p].val = t[p].tag = c;
return;
}
//这里不用pushdown,因为我们是区间覆盖,pushdown没什么用
// pushdown (p);
int mid = (t[p].l + t[p].r) >> 1;
if (l <= mid) modify (ls, l, r, c);
if (mid < r) modify (rs, l, r, c);
}
int query (int p, int pos) {
if (t[p].l == t[p].r) {
return t[p].val;
}
pushdown (p);
int mid = (t[p].l + t[p].r) >> 1;
if (pos <= mid) return query (ls, pos);
else return query (rs, pos);
}
int main () {
cin >> n >> S;
memset (f, 0x3f, sizeof f);
f[0][0] = f[0][1] = 0;
L[0] = R[0] = 0;
build (1, -100000, 100000);
for (int i = 1; i <= n; i ++) {
cin >> L[i] >> R[i];
int j = query (1, L[i]);
f[i][0] = min (abs (L[j] - L[i]) + f[j][0], abs (R[j] - L[i]) + f[j][1]);
j = query (1, R[i]);
f[i][1] = min (abs (L[j] - R[i]) + f[j][0], abs (R[j] - R[i]) + f[j][1]);
modify (1, L[i], R[i], i);
}
ll ans = min (f[n][0] + abs (L[n] - S), f[n][1] + abs (R[n] - S));
cout << ans << endl;
return 0;
}