AtCoder Beginner Contest 177 F - I hate Shortest Path Problem

题目链接
Problem Statement
There is a grid of squares with H + 1 H+1 H+1 horizontal rows and W W W vertical columns.

You will start at one of the squares in the top row and repeat moving one square right or down. However, for each integer i i i from 1 1 1 through H H H, you cannot move down from the A i A_i Ai-th, ( A i + 1 ) (Ai+1) (Ai+1)-th, … \dots , B i B_i Bi-th squares from the left in the i i i-th row from the top.

For each integer k k k from 1 1 1 through H H H, find the minimum number of moves needed to reach one of the squares in the ( k + 1 ) (k+1) (k+1)-th row from the top. (The starting square can be chosen individually for each case.) If, starting from any square in the top row, none of the squares in the ( k + 1 ) (k+1) (k+1)-th row can be reached, print − 1 -1 1 instead.

Constraints

  • 1 ≤ H , W ≤ 2 × 1 0 5 1≤H,W≤2×10^5 1H,W2×105
  • 1 ≤ A i ≤ B i ≤ W 1≤A_i≤B_i≤W 1AiBiW

Input
Input is given from Standard Input in the following format:

H H H W W W
A 1 A_1 A1 B 1 B_1 B1
A 2 A_2 A2 B 2 B_2 B2
. . _.^. ..
A H A_H AH B H B_H BH

Output
Print H H H lines. The i i i-th line should contain the answer for the case k = i k=i k=i.

Sample Input 1

4 4
2 4
1 1
2 3
2 4

Sample Output 1

1
3
6
-1

Let ( i , j ) (i,j) (i,j) denote the square at the i i i-th row from the top and j j j-th column from the left.

For k = 1 k=1 k=1, we need one move such as ( 1 , 1 ) → ( 2 , 1 ) (1,1) → (2,1) (1,1)(2,1).

For k = 2 k=2 k=2, we need three moves such as ( 1 , 1 ) → ( 2 , 1 ) → ( 2 , 2 ) → ( 3 , 2 ) (1,1)→(2,1)→(2,2)→(3,2) (1,1)(2,1)(2,2)(3,2).

For k = 3 k=3 k=3, we need six moves such as ( 1 , 1 ) → ( 2 , 1 ) → ( 2 , 2 ) → ( 3 , 2 ) → ( 3 , 3 ) → ( 3 , 4 ) → ( 4 , 4 ) (1,1)→(2,1)→(2,2)→(3,2)→(3,3)→(3,4)→(4,4) (1,1)(2,1)(2,2)(3,2)(3,3)(3,4)(4,4).

For k = 4 k=4 k=4, it is impossible to reach any square in the fifth row from the top.

建一棵线段树,位置 x x x的值 t [ x ] t[x] t[x]为从第一行到第 i i i行需要在水平方向移动的最小距离。
n o w now now表示从第一行到能到达第 i i i行的列号的最小值。
对于第 i i i行,设第 i − 1 i-1 i1行禁止向下走的区间为 [ l , r ] [l,r] [l,r],则:

  • 如果 n o w < l now< l now<l,则对于区间 [ l , r ] [l,r] [l,r],由于禁止向下走,不能直接从 i − 1 i-1 i1行转移过来,而是从 ( i − 1 , l − 1 ) (i-1,l-1) (i1,l1)转移过来,对于 x ∈ [ l , r ] x\in[l,r] x[l,r] t [ x ] t[x] t[x]需要在 t [ l − 1 ] t[l-1] t[l1]的基础上加上 x − l + 1 x-l+1 xl+1
  • 如果 n o w ∈ [ l , r ] now\in [l,r] now[l,r],则第 i i i行位于区间 [ n o w , r ] [now,r] [now,r]内的所有点都不能到达,直接设为 ∞ \infty ,将 n o w now now设为 r + 1 r+1 r+1
  • 如果 n o w ∈ ( r , m ] now\in(r,m] now(r,m],则对结果无影响。
  • 如果 n o w > m now>m now>m则不可到达第 i i i行, i i i行及后面都输出 − 1 -1 1

然后答案为线段树维护区间内的最小值。
时间复杂度为 O ( H log ⁡ W ) O(H\log W) O(HlogW)

#include<bits/stdc++.h>

#define INF 0x3f3f3f3f
using namespace std;

inline int qr() {
    int f = 0, fu = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-')fu = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        f = (f << 3) + (f << 1) + c - 48;
        c = getchar();
    }
    return f * fu;
}


const int N = 2e5 + 10;

struct Seg_Tree {
    struct T {
        int l, r, ans, add;
    } t[N * 4];

    void build(int p, int l, int r) {
        t[p].l = l, t[p].r = r, t[p].ans = t[p].add = 0;
        if (l == r) return;
        int mid = (l + r) >> 1;
        build(p << 1, l, mid);
        build(p << 1 | 1, mid + 1, r);
    }

    void spread(int p) {
        if (t[p].add) {
            t[p << 1].ans = t[p].l - t[p].add;
            t[p << 1].add = t[p].add;
            t[p << 1 | 1].ans = ((t[p].l + t[p].r) >> 1) + 1 - t[p].add;
            t[p << 1 | 1].add = t[p].add;
            t[p].add = 0;
        }
    }

    void change(int p, int l, int r, int c) {
        if (l <= t[p].l && t[p].r <= r) {
            t[p].ans = t[p].l - c, t[p].add = c;
            return;
        }
        spread(p);
        int mid = (t[p].l + t[p].r) >> 1;
        if (l <= mid)change(p << 1, l, r, c);
        if (r > mid)change(p << 1 | 1, l, r, c);
        t[p].ans = min(t[p << 1].ans, t[p << 1 | 1].ans);
    }

    int ask(int p, int x) {
        if (t[p].l == t[p].r)return x - t[p].ans;
        spread(p);
        int mid = (t[p].l + t[p].r) >> 1;
        return x <= mid ? ask(p << 1, x) : ask(p << 1 | 1, x);
    }
} tr;

int n, m, now = 1;

int main() {
    n = qr(), m = qr();
    tr.build(1, 1, m);
    for (int i = 1; i <= n; ++i) {
        int l = qr(), r = qr();
        if (now >= l && now <= r)tr.change(1, now, r, -INF), now = r + 1;
        else if (now < l)tr.change(1, l, r, tr.ask(1, l - 1));
        if (now > m) {
            for (int j = i; j <= n; ++j)puts("-1");
            return 0;
        }
        printf("%d\n", i + tr.t[1].ans);
    }
    return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_sky123_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值