【Atcoder Beginner Contest 177 】 F - I hate Shortest Path Problem 思路+线段树
AtCoder Beginner Contest 177 F - I hate Shortest Path Problem
题意
给出一个高为 n+1,宽为 m 的矩形格子。现在你可以选择第一行的任意一个位置为起点,开始移动,只能向右或者下移动。在 [1,n] 行每行都有一个区间[l,r],在这个区间中,不能向下移动。
问到达第[2,n+1]行的最少移动次数。
思路
参考博客:
https://www.cnblogs.com/Go7338395/p/13591270.html
https://blog.csdn.net/qq_45323960/article/details/108302036
因为到达某一行,向下移动的次数是固定的,所以我们先看向右移动的次数,定义 dis[i][x] 表示到达第 i 行,第 x 个格子的最小步数。
初始化 dis[1][1,2,3,...,m] 为0。
这时我们来看,对于当前格子最好的走法,肯定是如果能向下走,直接向下走,否则向右走直到可以向下走,然后向下走。
来看具体过程。
假如现在在第4行,dis[4]为:
0 1 2 0 0 0
这一行的话,如果是区间[4,5]不能向下走。
那么第 5 行的第4,5列应该是从第5行的第3列走过去。
那么我们就要更新[4,5]这段区间为以 dis[ 3 ]+1 为开头的公差为 1 的数列。
具体一些:
如果第 i 行最小可以到达的列数为 now,那么我们看,如果第 i 行不可以向下走的区间为[l,r]。
-
l<=now && now <=r ,第i+1行可以到达的最小列数为 r+1,这时更新dis[i+1][now---r]为无穷大。
-
当now < l的时候,我们就更新[l,r] 为以dis[i][l-1]+1为首项公差为1的数列。
-
dis[i+1]的最小值+ i 就是答案。
对于更改操作,使用线段树。
具体可以看代码
/*
* @Autor: valk
* @Date: 2020-07-17 18:36:08
* @LastEditTime: 2020-09-25 16:31:41
* @Description: 如果邪恶 是华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
*/
#include <algorithm>
#include <iostream>
#include <map>
#include <math.h>
#include <queue>
#include <set>
#include <stack>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const int seed = 12289;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 10;
struct note {
int tag, minn;//minn表示当前区间的最小值,tag 表示当前区间[l,r]是一个以tag 为首项的公差为1的等差数列。
} node[N * 4];
void build(int rt, int l, int r)
{
if (l == r)
return;
int mid = (l + r) >> 1;
build(rt * 2, l, mid);
build(rt * 2 + 1, mid + 1, r);
}
void pushdown(int rt, int l, int r)
{
if (!node[rt].tag) {
return;
}
int mid = (l + r) >> 1;
node[rt * 2].tag = node[rt].tag;
node[rt * 2 + 1].tag = node[rt].tag + mid - l + 1;
node[rt * 2].minn = node[rt].tag;
node[rt * 2 + 1].minn = node[rt * 2 + 1].tag;
node[rt].tag = 0;
}
void update(int rt, int l, int r, int qs, int qe, int val)
{
if (qs <= l && qe >= r) {
node[rt].minn = node[rt].tag = val;
return;
}
pushdown(rt, l, r);
int mid = (l + r) / 2;
if (qs <= mid)
update(rt * 2, l, mid, qs, qe, val);
if (qe > mid)
update(rt * 2 + 1, mid + 1, r, qs, qe, val + max(0, mid - max(qs, l) + 1));//这里注意考虑查询区间和当前区间的相交情况,来递归转移
node[rt].minn = min(node[rt * 2].minn, node[rt * 2 + 1].minn);
}
int query(int rt, int l, int r, int pos)
{
if (l == r) {
return node[rt].minn;
}
pushdown(rt, l, r);
int mid = (l + r) / 2;
if (pos <= mid) {
return query(rt * 2, l, mid, pos);
} else {
return query(rt * 2 + 1, mid + 1, r, pos);
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
build(1, 1, m);
int now = 1;
for (int i = 1; i <= n; i++) {
int l, r;
scanf("%d%d", &l, &r);
if (now < l) {
update(1, 1, m, l, r, query(1, 1, m, l - 1) + 1);
} else if (l <= now && now <= r) {
update(1, 1, m, now, r, 1000000);
now = r + 1;
}
int ans = node[1].minn;
if (ans >= m) {
printf("-1\n");
} else {
printf("%d\n", ans + i);
}
}
return 0;
}