---
# System prepended metadata

title: ABC237-G Range Sort Query
tags: [AtCoder]

---


$Q\ \le\ 2\times 10^5$ で解けるの凄い！

## 問題概要

https://atcoder.jp/contests/abc237/tasks/abc237_g

長さ$N$の順列$P$を与えます。以下の$Q$個のクエリを処理します。

クエリ`t l r`
- $t = 1$ならば、$P_{l}, P_{l + 1}, \dots, P_{r - 1}, P_{r}$を昇順にソートする
- $t = 2$ならば、$P_{l}, P_{l + 1}, \dots, P_{r - 1}, P_{r}$を降順にソートする

すべてのクエリを処理した後、$P_i = X$を満たす$i$を報告してください


#### 制約

$1\ \le\ N, Q\ \le\ 2\times 10^5$

## 情報を削る・観察

普通にクエリを処理するのは難しいと感じました。[^1]

最終的に$P_i = X$を満たす$i$が求まれば良いことを念頭に、クエリにおいてそのような$i$がどう移動するかを観察します。

- 区間に該当する$i$が含まれていない場合
    - 移動しない
- 区間に$i$が含まれている時
    - 昇順ソートの場合、その区間に含まれる$X$よりも小さい値より後ろに移動する。また、その区間に含まれる$X$より大きい値より前に移動する
    - 降順ソートの場合、反対になる(大きい値の後ろ、小さい値の前に移動する)

よって、各値が$X$より小さい/等しい/大きいかのみが重要であることがわかります。

ここから、順列では無く、以下の数列$P'$上でクエリを処理します。

$$P'_i = 
\begin{cases}
    -1 & \text{if}\ P_i < X \\
    0 & \text{if}\ P_i = X \\
    1 & \text{else}
\end{cases}$$

## P'上でのクエリ処理

観察で見た通り、クエリ処理では区間に含まれる$X$より小さい数と区間に含まれる$X$より大きい数が重要でした。$P'$上ではこれを「区間に含まれる$-1$の数」と「区間に含まれる$1$の数」と言い換えることができます
- このような数を求めるのは累積和等でできますね。実際はクエリ処理で要素の移動が発生するので、今回はセグメント木等の値の変更に強いデータ構造を利用することになります。

クエリの区間に含まれる$-1$の数を$a$、$0$の数を$b$、$1$の数を$c$とします。[^2]

クエリが昇順ソートだった場合、区間$P_l, P_{l + 1}, \dots, P_{r - 1}, P_{r}$は$P_l, P_{l + 1}, \dots, P_{l + a}$は$-1$、$P_{l + a + 1}, \dots, P_{l + a + b}$は$0$、$P_{l + a + b}, \dots, P_{l + a + b + c = r}$は$1$になります。このような数列の変更は、Range Update Queryを$3$回行えば実現することが可能です。

降順ソートだった場合も同様に考えることができます。

以上により、例えば遅延セグメント木を利用する事で$P'$上のクエリ処理が実現することができました。

## 提出コード

自分の遅延セグ木、RUQライブラリがかなりお粗末だった。

```cpp=
using tup = tuple<i32, i32, i32>;
 
tup operator+(const tup& a, const tuple<i32, i32, i32>& b) {
    auto [x, y, z] = a;
    auto [p, q, r] = b;
    return tup{ x + p, y + q, z + r };
}
 
tup operator*(i32 k, const tup& a) {
    auto [x, y, z] = a;
    return tup{ k * x, k * y, k * z };
}
 
using vM = zawa::rangeAddMonoid<tuple<i32, i32, i32>>;
 
struct oM {
    using valueType = tuple<i32, i32, i32>;
    static constexpr valueType identity{ -1, -1, -1 };
    static valueType operation([[maybe_unused]] const valueType& a, const valueType& b) {
        return b;
    }
};
 
struct action {
    using valueMonoid = vM;
    using operatorMonoid = oM;
    static valueMonoid::valueType mapping(const valueMonoid::valueType& a, const tuple<i32, i32, i32>& b) {
        return valueMonoid::valueType{ a.size * b, a.size };
    }
};
 
void main_() {
    i32 N, Q, X; in(N, Q, X);
    vector<vM::valueType> init(N);
    tuple<i32, i32, i32> pls = { 0, 0, 1 }, mns = { 1, 0, 0 }, eq = { 0, 1, 0 };
    for (i32 i = 0 ; i < N ; i++) {
        i32 p; in(p);
        init[i].size = 1;
        if (p < X) init[i].value = mns;
        if (p == X) init[i].value = eq;
        if (p > X) init[i].value = pls;
    }
    zawa::lazySegmentTree<action> S(init);
    times(Q) {
        i32 c, l, r; in(c, l, r);
        l--;
        if (c == 1) {
            auto [m, e, p] = S.prod(l, r).value;
            assert(l + m + e + p == r);
            S.update(l, l + m, mns);
            S.update(l + m, l + m + e, eq);
            S.update(l + m + e, l + m + e + p, pls);
        }
        else if (c == 2) {
            auto [m, e, p] = S.prod(l, r).value;
            assert(l + m + e + p == r);
            S.update(l, l + p, pls);
            S.update(l + p, l + p + e, eq);
            S.update(l + p + e, l + p + e + m, mns);
        }
        else {
            assert(!"input failed");
        }
    }
    i32 ans = -1;
    for (i32 i{} ; i <= N ; i++) if (get<1>(S.prod(0, i).value) == 1) {
        ans = i;
        break;
    }
    out(ans);
}
```


[^1]: Library Checkerに区間ソートをする問題がありますが、自分はよくわかってないです。[問題](https://judge.yosupo.jp/problem/point_set_range_sort_range_composite)

[^2]: $b$は$0$か$1$のどちらかとなります