# ARC159-D LIS 2
2056diff
https://atcoder.jp/contests/arc159/tasks/arc159_d
## 解法
$i$番目の共通区間を部分列に含めるとき、$R_{i}$まで採用してしまっても損をしない。
このことを踏まえて動的計画法を設計する。
**定義**
$\text{dp}[i] :=$ $i$番目までの区間を考慮したときのLISの長さの最大。
**テーブルの大きさ**
$N$
**初期値**
$\text{dp}[i] =
\begin{cases}
0 & i = 0 \\
-\infty & \text{else} \\
\end{cases}$
**遷移**
$j = 0, 1, 2, \dots, i - 1$について
- $R_{j}\ge R_{i}$なら貰えない
- $R_{j}\lt L_{i}$なら$\text{dp}[j] + R_{i} - L_{i} + 1$貰える
- それ以外は$\text{dp}[j] + R_{i} - R_{j}$貰える
これを愚直にやると$\Theta(N^2)$かかる。そこでLISのdpをセグ木で高速化するのと同じ様なアイデアでこのdpを高速化できないか考える。
$(0, \text{dp}[0]), (R_{1}, \text{dp}[1]), (R_{2}, \text{dp}[2]), \dots, (R_{i - 1}, \text{dp}[i - 1])$からなる$i$個の点が座標平面上にあるとする。$x$座標が$k$である点の中での最大の$y$座標を$Y_{k}$と呼ぶことにする。点が無いなら$Y_{k} = -\infty$とする。$Y$を用いて$\text{dp}[i]$の計算は以下のように言い換えられる。
> $\text{dp}[i] = R_{i} - L_{i} + 1 + \max\{ Y_{0}, Y_{1}, \dots, Y_{L_{i} - 1}, Y_{L_{i}} - 1, Y_{L_{i} + 1} - 2, \dots, Y_{R_{i} - 1} - R_{i} - L_{i}\}$
$\max\{Y_{0}, Y_{1}, \dots, Y_{L_{i} - 1}\}$と$\max\{Y_{L_{i}} - 1, Y_{L_{i} + 1} - 2, \dots, Y_{R_{i} - 1} - R_{i} - L_{i}\}$を別々に計算してやれば良い。前者はそのまま区間max、後者は$x$座標-$y$座標の区間maxを取れば計算できる。
$Y_{k} \ne -\infty$となる$k$は高々$N$個である。そのような$k$のみを考慮するようにすれば(=$R$で座圧せよ)、以上をセグメント木で効率的に計算できるようになる。全体の時間計算量は$\Theta(N\log N)$と見積もれる。
## 実装
```cpp=
#include <bits/stdc++.h>
template <class S>
struct SegmentTree {
using T = typename S::T;
int n;
std::vector<T> data;
SegmentTree(int N) : n{N}, data(2 * n, S::identity()) {}
void update(int i, const T& v) {
assert(0 <= i and i < n);
data[n + i] = S::operation(data[n + i], v);
for (int j{(n + i) >> 1} ; j ; j >>= 1) {
data[j] = S::operation(data[j << 1 | 0], data[j << 1 | 1]);
}
}
T prod(int l, int r) const {
assert(0 <= l and l <= r and r <= n);
T L{S::identity()}, R{S::identity()};
for (l += n, r += n ; l < r ; l >>= 1, r >>= 1) {
if (l & 1) {
L = S::operation(L, data[l]);
l++;
}
if (r & 1) {
r--;
R = S::operation(data[r], R);
}
}
return S::operation(L, R);
}
};
const long long INF{(long long)-2e9};
struct MAX {
using T = long long;
static T identity() { return INF; }
static T operation(T l, T r) { return std::max(l, r); }
};
struct MAX2 {
using T = std::pair<long long, int>;
static T identity() { return { INF, -1 }; }
static T operation(T l, T r) { return l.first - l.second < r.first - r.second ? r : l; }
};
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int N;
std::cin >> N;
std::vector<int> L(N), R(N);
for (int i{} ; i < N ; i++) {
std::cin >> L[i] >> R[i];
}
std::vector<int> comp{R};
comp.push_back(0);
std::sort(comp.begin(), comp.end());
comp.erase(std::unique(comp.begin(), comp.end()), comp.end());
assert(comp[0] == 0);
SegmentTree<MAX> seg(comp.size());
SegmentTree<MAX2> teg(comp.size());
seg.update(0, 0);
teg.update(0, { 0, 0 });
for (int i{} ; i < N ; i++) {
auto l{std::lower_bound(comp.begin(), comp.end(), L[i]) - comp.begin()};
auto r{std::lower_bound(comp.begin(), comp.end(), R[i]) - comp.begin()};
auto v1{seg.prod(0, l)};
auto v2{teg.prod(l, r)};
assert(v2 == MAX2::identity() or (L[i] <= v2.second and v2.second < R[i]));
long long val{std::max(v1 + R[i] - L[i] + 1, v2.first + R[i] - v2.second )};
seg.update(r, val);
teg.update(r, { val, R[i] });
}
std::cout << seg.prod(0, comp.size()) << '\n';
}
```
[提出](https://atcoder.jp/contests/arc159/submissions/58329331)
## 感想
かなり取り組みやすかった。
セグ木のソラ書きを失敗して一箇所バグらせた。船降ります。