【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]。

  1. l<=now && now <=r ,第i+1行可以到达的最小列数为 r+1,这时更新dis[i+1][now---r]为无穷大。

  2. 当now < l的时候,我们就更新[l,r] 为以dis[i][l-1]+1为首项公差为1的数列。

  3. 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;
}
posted @ 2020-09-25 16:45  Valk3  阅读(157)  评论(0编辑  收藏  举报