设 \(f_{i,j}\) 为从第 \(1\) 行到 \((i + 1, j)\) 的最短路。
因为我们并不关心最后到达的是哪一个格子,于是强制 \(f_{i,j}\) 为必须从 \((i, j)\) 往下走一格到 \((i + 1, j)\) 的最短路。
有转移:
\[f_{i,r+1} \gets f_{i-1,j} + r + 2 - j, j \in [l, r] \]\[f_{i,j} \gets f_{i-1,j} + 1, j \notin [l, r] \]\([l, r]\) 表示第 \(i\) 行被 ban 掉的区间。
这个东西显然能用线段树优化。具体地,枚举到第 \(i\) 行,线段树每个叶子结点 \([j,j]\) 存 \(f_{i,j} - j\) 的最小值。对于第一种转移,区间查最小值,单点修;对于第二种转移,区间加 \(1\)。注意到 \(j \in [l,r], f_{i,j}\) 不合法,把它们加上一个 \(+\infty\) 即可。
时间复杂度线性对数。
code
// Problem: F - I hate Shortest Path Problem
// Contest: AtCoder - AtCoder Beginner Contest 177
// URL: https://atcoder.jp/contests/abc177/tasks/abc177_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 200100;
const ll inf = 0x3f3f3f3f3f3f3f3fLL;
int n, m;
struct node {
int l, r;
} a[maxn];
namespace SGT {
ll tree[maxn << 2], tag[maxn << 2], mn[maxn << 2];
inline void pushup(int x) {
tree[x] = min(tree[x << 1], tree[x << 1 | 1]);
mn[x] = min(mn[x << 1], mn[x << 1 | 1]);
}
inline void pushdown(int x) {
if (!tag[x]) {
return;
}
tree[x << 1] += tag[x];
tree[x << 1 | 1] += tag[x];
mn[x << 1] += tag[x];
mn[x << 1 | 1] += tag[x];
tag[x << 1] += tag[x];
tag[x << 1 | 1] += tag[x];
tag[x] = 0;
}
void build(int rt, int l, int r) {
if (l == r) {
mn[rt] = -l;
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushup(rt);
}
void update(int rt, int l, int r, int ql, int qr, int x) {
if (ql > qr) {
return;
}
if (ql <= l && r <= qr) {
tree[rt] += x;
mn[rt] += x;
tag[rt] += x;
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
pushup(rt);
}
void modify(int rt, int l, int r, int x, ll y) {
if (l == r) {
tree[rt] = min(tree[rt], y);
mn[rt] = min(mn[rt], y - l);
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
(x <= mid) ? modify(rt << 1, l, mid, x, y) : modify(rt << 1 | 1, mid + 1, r, x, y);
pushup(rt);
}
ll query(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return mn[rt];
}
pushdown(rt);
ll mid = (l + r) >> 1, res = inf;
if (ql <= mid) {
res = min(res, query(rt << 1, l, mid, ql, qr));
}
if (qr > mid) {
res = min(res, query(rt << 1 | 1, mid + 1, r, ql, qr));
}
return res;
}
}
void solve() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &a[i].l, &a[i].r);
}
SGT::build(1, 1, m);
for (int i = 1; i <= n; ++i) {
int l = a[i].l, r = a[i].r;
SGT::update(1, 1, m, 1, l - 1, 1);
SGT::update(1, 1, m, r + 1, m, 1);
ll t = SGT::query(1, 1, m, l, r);
SGT::update(1, 1, m, l, r, 2e9);
if (r < m) {
SGT::modify(1, 1, m, r + 1, t + r + 2);
}
ll res = SGT::tree[1];
printf("%lld\n", res > 1e9 ? -1 : res);
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}