题目描述:

# P6007 [USACO20JAN] Springboards G

## 题目描述

Bessie 在一个仅允许沿平行于坐标轴方向移动的二维方阵中。她从点 $(0,0)$ 出发,想要到达 $(N,N)$($1 \leq N \leq 10^9$)。为了帮助她达到目的,在方阵中有 $P$($1 \leq P \leq 10^5$)个跳板。每个跳板都有其固定的位置 $(x_1,y_1)$,如果 Bessie 使用它,会落到点 $(x_2,y_2)$。

Bessie 是一个过程导向的奶牛,所以她仅允许她自己向上或向右行走,从不向左或向下。类似地,每个跳板也设置为不向左或向下。Bessie 需要行走的距离至少是多少?

## 输入格式

输入的第一行包含两个空格分隔的整数 $N$ 和 $P$。

以下 $P$ 行每行包含四个整数 $x_1,y_1,x_2,y_2$,其中 $x_1 \leq x_2$ 且 $y_1 \leq y_2$。

所有跳板的位置和目标位置均不相同。

## 输出格式

输出一个整数,为 Bessie 到达点 $(N,N)$ 需要行走的最小距离。

## 输入输出样例 #1

### 输入 #1

```
3 2
0 1 0 2
1 2 2 3
```

### 输出 #1

```
3
```

## 说明/提示

### 样例解释

Bessie 的最佳路线为:

- Bessie 从 (0,0) 走到 (0,1)(1 单位距离)。
- Bessie 跳到 (0,2)。
- Bessie 从 (0,2) 走到 (1,2)(1 单位距离)。
- Bessie 跳到 (2,3)。
- Bessie 从 (2,3) 走到 (3,3)(1 单位距离)。

Bessie 总共走过的路程为 3 单位距离。

### 子任务

- 测试点 $2 \sim 5$ 满足 $P \leq 1000$。
- 测试点 $6 \sim 15$ 没有额外限制。

解析:

先将所有坐标的 y 坐标离散化。

然后把所有坐标按照 x 为第一关键字, y 为第二关键字排序。

从前往后遍历, 用 dpi​ 表示从 (0,0) 到 xi​,yi​ 的答案。

怎么更新信息?

考虑用 k 节点更新 i 节点。

如果 k 节点可以直接用跳板直接跳到 i 节点, 那么 dp 值相同。

否则要一步一步走过去。如果能更新, 那么 dpi​=dpk​+xi​−xk​+yi​−yk​

那么 dpi​−xi​−yi​=dpk​−xk​−yk​

如果设 fi​=dpi​−xi​−yi​,那么 fi​=fk​ !

那么现在丢掉 dp 数组然后使用 f 数组。

现在如果k 节点可以直接用跳板直接跳到 i 节点

fi​=dpi​−xi​−xj​=dpk​−xi​−yi​=fk​+xk​+yk​−xi​−yi​

那么考虑谁能够更新他呢?

只有 x 坐标小于等于他而且 y 坐标小于等于他的节点可以更新。

这时显然 x 坐标小于等于 i 节点已经满足, 而 y 坐标没有满足。 现在要求 y 坐标处于 0...yi​ 的节点。

fi​ 是 min(f1,2...i−1​) , 显然每次把 f 值丢进树状数组里面就可以单次 O(logn) 维护了

如果 i 是一个跳板的末端, 直接更新一下就好了qwq

最后答案就是 min(fi​+n+n) (如果把他当作一个节点, 那么 f 值就是 min(fi​), 所以答案是 min(fi​+n+n))

code:

#include<bits/stdc++.h>
#define N 200010
using namespace std;
int n, m, a[N], fr[N], to[N], Ans;
struct node {
	int x, y, yy, id;
} p[N];
bool cmp(node aa, node bb) {
	return aa.x == bb.x ? aa.y < bb.y : aa.x < bb.x;
}
bool ycmp(node aa, node bb) {
	return aa.y < bb.y;
}
int ans[N];
void add(int x, int val) {
	for(; x <= m * 2; x += (x & -x)) ans[x] = min(ans[x], val);
} 
int qzh(int x) {
	int Ans = 0; 
	for(; x; x -= (x & -x)) Ans = min(Ans, ans[x]);
	return Ans;
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; i++) {
		scanf("%d%d", &p[i].x, &p[i].y), p[i].id = i;
		scanf("%d%d", &p[i + m].x, &p[i + m].y), p[i + m].id = i;
	}
	sort(p + 1, p + m * 2 + 1, ycmp);
	p[0].y = -1;
	for(int i = 1; i <= 2 * m; i++) p[i].yy = p[i - 1].yy + (p[i - 1].y != p[i].y);
	sort(p + 1, p + m * 2 + 1, cmp);
	for(int i = 1; i <= m * 2; i++) {
		if(fr[p[i].id]) to[p[i].id] = i;
		else fr[p[i].id] = i;
	}
	for(int i = 1; i <= m * 2; i++) {
		a[i] = min(a[i], qzh(p[i].yy));
		if(fr[p[i].id] == i) a[to[p[i].id]] = min(a[to[p[i].id]], 
			a[i] + p[i].x + p[i].y - p[to[p[i].id]].x - p[to[p[i].id]].y);
		add(p[i].yy, a[i]);
		Ans = min(Ans, a[i]);
	}
	printf("%d\n", Ans + n * 2);
	return 0;
}

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐