P10964 Fence Obstacle Course 题解
wjyppm1403 · · 题解
题目大意:
给定
n 个平台,第i 个平台的高度为i ,其左右端点为L_i,R_i 。现在你在第n 个平台上,横坐标为S ,可以选择在平台的左端点或右端点跳到下一个平台。你需要设计一个移动方案使得移动到(0,0) 点时水平移动距离最小。输出最小水平移动距离。
下面我们将栅栏用“平台”一词来代替。
根据题意,能够发现奶牛跑到第
如果枚举从哪块模板跳过来复杂度可能会退化,我们可以考虑递推的思想,枚举下面的跳板 你又不能穿过去这个跳板)。
转移方程如下,其中
初始化
这样的时间复杂度是
观察转移方程,我们发现瓶颈就在于枚举一个在区间范围的合法值
我们可以从小到大枚举,每一次我们查询左端点被下方的哪一个点覆盖,右端点同理,记录下来,查询完后我们对
时间复杂度为
本题的坐标有负值,可以考虑离散化或都加上一个偏移量,这里采用后者方法。注意区间覆盖用懒标记优化复杂度,线段树不应当有 pushup,因为这里维护的是单独区间颜色信息(当然也可以用珂朵莉树,但是复杂度不一定了)。
代码如下:
#include <bits/stdc++.h>
#define ls p << 1
#define rs p << 1 | 1
#define getn(x) (x + 100001) // 坐标偏移,将负坐标转换为正数方便处理
using namespace std;
constexpr int MN = 2e6 + 15;
int f[MN][2], lc[MN], rc[MN], n, s;
// lc,rc为记录的编号
struct zhalan {
int l, r;
} zl[MN];
struct segtree {
int l, r, val, cov;
} t[MN << 2];
void pushdown(int p) {
if (t[p].cov) {
t[ls].val = t[rs].val = t[ls].cov = t[rs].cov = t[p].cov;
t[p].cov = 0;
}
}
void build(int p, int l, int r) {
t[p].l = l;
t[p].r = r;
if (l == r) return;
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}
void cover(int p, int fl, int fr, int k) {
if (t[p].l >= fl && t[p].r <= fr) {
t[p].cov = t[p].val = k;
return;
}
pushdown(p);
int mid = (t[p].l + t[p].r) >> 1;
if (mid >= fl) cover(ls, fl, fr, k);
if (mid < fr) cover(rs, fl, fr, k);
}
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;
return pos <= mid ? query(ls, pos) : query(rs, pos);
}
int main() {
memset(f, 0x3f, sizeof(f)); // 初始化为极大值
build(1, 1, 2e6);
zl[0].l = zl[0].r = getn(0); //注意这里是getn(0)! 原点也要偏移!
cin >> n >> s;
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
zl[i].l = getn(a);//偏移l,r
zl[i].r = getn(b);
lc[i] = query(1, zl[i].l); // 查询当前平台i的左端点下方第一个平台编号
rc[i] = query(1, zl[i].r); // 查询当前平台i的右端点下方第一个平台编号
cover(1, zl[i].l, zl[i].r, i); // 将区间[zl[i].l, zl[i].r]标记为平台i
}
f[n][0] = abs(getn(s) - zl[n].l);
f[n][1] = abs(zl[n].r - getn(s));
for (int i = n; i >= 1; i--) {// 递推,启动!
// 从当前平台i的左端点跳到下方平台lc[i]的左右端点
f[lc[i]][0] = min(f[lc[i]][0], f[i][0] + abs(zl[i].l - zl[lc[i]].l));
f[lc[i]][1] = min(f[lc[i]][1], f[i][0] + abs(zl[i].l - zl[lc[i]].r));
// 从当前平台i的右端点跳到下方平台rc[i]的左右端点
f[rc[i]][0] = min(f[rc[i]][0], f[i][1] + abs(zl[i].r - zl[rc[i]].l));
f[rc[i]][1] = min(f[rc[i]][1], f[i][1] + abs(zl[i].r - zl[rc[i]].r));
}
cout << f[0][0]; // 这里也可以是f[0][1]
return 0;
}