标签:printf ems ++i struct 覆盖 string operator strong 编号
在坐标系中有\(n\)个矩形(保证矩形的四边平行于坐标轴),一个人起始位置在\((0,0)\),需要到达\((X_t,0)\),规定这个人不能穿过任何一个矩形(但可以贴着矩形的边界行走),求最短路线的长度
\(n\leq 5\times 10^5\)
首先有一种行走方式,可以保证最优解一定满足这种行走方式
那就是:在遇到一个矩形时,贴着矩形的边界避开它。这里可以感性的理解一下,应该很好懂
我们还能发现,无论怎么行走,与\(x\)轴平行的路径长度对答案的贡献一定是\(X_t\):不然就会走回头路
在每次避开一个矩形之后,我们一定会来到当前矩形的右上角或右下角,然后等待进行下一步决策
这样我们就可以进行\(DP\)了:设\(DP[x][0/1]\)为当前位于第\(x\)个矩形的右上角\(/\)右下角的答案
那么\(DP[x][0/1]=min(DP[nxt[x][0/1]][0], DP[nxt[x][0/1]][1])\)
这里的\(nxt[x][0/1]\)代表当前位于第\(x\)个矩形的右上角\(/\)右下角,继续向前走所接触到的第一个矩形的编号
若当前的矩形没有\(nxt\)了,那么说明向前走没有障碍物了,那么此时\(DP[x][0/1]=abs(y)\)(\(y\)为当前矩阵右上角\(/\)右下角的纵坐标)
这里可以用记忆化搜索的形式实现,很好写
可以发现我们每次搜到一个障碍物,它的答案就确定并被记忆化了,所以可以发现状态数是\(O(n)\)的
现在复杂度瓶颈变成了求\(nxt\)数组
朴素的求\(nxt\)数组是\(O(n^2)\)的
我们考虑用线段树进行优化
由于\(DP\)中状态只与矩形的右边界有关,我们把所有右边界直线提出来按横坐标排序
将纵坐标离散化,开一颗维护区间覆盖的线段树
倒序枚举所有右边界,每次在线段树中查得一个答案更新区间
复杂度\(O(n \log n)\),具体实现代码很好懂
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int n, t, Xt;
int h[N], f[N][2], nxt[N][2];
struct line {
int x, u, d;
bool operator < (const line &T) const { return x < T.x; }
} e[N];
inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }
inline int abs(int x) { return x < 0 ? -x : x; }
void discrete() {
h[++t] = 0;
sort(h + 1, h + t + 1);
t = unique(h + 1, h + t + 1) - h - 1;
}
struct SegTree {
#define ls x << 1
#define rs x << 1 | 1
int val[N << 2], tag[N << 2];
void pushdown(int x) {
if (tag[x]) {
val[ls] = val[rs] = tag[x];
tag[ls] = tag[rs] = tag[x];
tag[x] = 0;
}
}
void update(int x, int l, int r, int ql, int qr, int v) {
if (ql <= l && r <= qr) {
val[x] = v, tag[x] = v;
return;
}
int mid = l + r >> 1;
pushdown(x);
if (ql <= mid)
update(ls, l, mid, ql, qr, v);
if (qr > mid)
update(rs, mid + 1, r, ql, qr, v);
}
int query(int x, int l, int r, int k) {
if (l == r) return val[x];
int mid = l + r >> 1;
pushdown(x);
if (k <= mid)
return query(ls, l, mid, k);
else
return query(rs, mid + 1, r, k);
}
#undef ls
#undef rs
} tr;
int DFS(int u, int to, int y) {
if (~f[u][to])
return f[u][to];
if (!nxt[u][to])
return f[u][to] = abs(y);
int ne = nxt[u][to];
int up = abs(e[ne].u - y), dw = abs(y - e[ne].d);
up += DFS(nxt[u][to], 0, e[ne].u);
dw += DFS(nxt[u][to], 1, e[ne].d);
return f[u][to] = min(up, dw);
}
int main() {
scanf("%d%d", &n, &Xt);
int a, b, c, d;
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d%d", &a, &b, &c, &d);
e[i] = (line){max(a, c), max(b, d), min(b, d)};
h[++t] = b, h[++t] = d;
}
discrete();
sort(e + 1, e + n + 1);
for (int i = n; i >= 1; --i) {
int a = lower_bound(h + 1, h + t + 1, e[i].d) - h;
int b = lower_bound(h + 1, h + t + 1, e[i].u) - h;
// printf("va: %d a: %d vb: %d b: %d\n", e[i].u, a, e[i].d, b);
nxt[i][0] = tr.query(1, 1, t, b);
nxt[i][1] = tr.query(1, 1, t, a);
tr.update(1, 1, t, a, b, i);
}
int id = lower_bound(h + 1, h + t + 1, 0) - h;
nxt[0][0] = nxt[0][1] = tr.query(1, 1, t, id);
// for (int i = 0; i <= n; ++i) printf("%d %d %d\n", i, nxt[i][0], nxt[i][1]);
memset(f, -1, sizeof f);
printf("%d\n", Xt + DFS(0, 0, 0));
return 0;
}
标签:printf ems ++i struct 覆盖 string operator strong 编号
原文地址:https://www.cnblogs.com/VeniVidiVici/p/11508777.html