Official

E - Filters Editorial by tatyam


\(x_1, x_2, \dots, x_Q\) のそれぞれに関数 \(f_1, f_2, \dots, f_N\) を順次適用させていく \(\Theta(NQ)\) の解法は \(NQ\) が最大 \(4 \times 10^{10}\) になるため TLE になります。
そこで、合成関数 \(F(x) = f_N(\dots f_2(f_1(x))\dots)\) がどのような形になるかを考えてみましょう。

\(t_i = 1\) のとき、 \(f_i\) はグラフを上下に移動させる操作、
\(t_i = 2\) のとき、 \(f_i\)\(a_i\) より小さい部分を \(a_i\) にする操作、
\(t_i = 3\) のとき、 \(f_i\)\(a_i\) より大きい部分を \(a_i\) にする操作であるので、
これらを組み合わせてできる関数は下の図のような形をしている、つまり、ある数 \(a, b, c\) が存在して \(F(x) = \min(c, \max(b, x + a))\) と表現できるだろうと予想できます。

これは以下のように証明できます。

\(t_i = 1\) のとき、\(f_i(x) = \min(\infty, \max(-\infty, x + a_i))\)
\(t_i = 2\) のとき、\(f_i(x) = \min(\infty, \max(a_i, x + 0))\)
\(t_i = 3\) のとき、\(f_i(x) = \min(a_i, \max(-\infty, x + 0))\) と、\(f_i(x)\)\(\min(c, \max(b, x + a))\) の形で表現できます。

また、\(f(x) = \min(c_1, \max(b_1, x + a_1)), g(x) = \min(c_2, \max(b_2, x + a_2))\) としたとき、これらの合成関数 \(g(f(x))\) は、

\[ \begin{aligned} g(f(x)) &= \min(c_2, \max(b_2, \min(c_1, \max(b_1, x + a_1)) + a_2))\\ &= \min(c_2, \max(b_2, \min(c_1 + a_2, \max(b_1 + a_2, x + a_1 + a_2))))\\ &= \min(\min(c_2, \max(b_2, c_1 + a_2)), \max(\max(b_2, \min(c_1 + a_2, b_1 + a_2)), x + (a_1 + a_2)))\\ \end{aligned} \]

と、\(\min(c, \max(b, x + a))\) の形で表現できます。

\(F(x)\)\(f_1, f_2, …, f_N\) の合成でできているので、\(\min(c, \max(b, x + a))\) の形で表現できることが証明できました。

次は、\(F(x) = \min(c, \max(b, x + a))\) と表したときの \(a, b, c\) の値を求めましょう。
これは、上の合成規則 \(g(f(x))\) に従って \(f_1, f_2, \dots, f_N\) を合成していけば計算できます。

\(a, b, c\) の値がわかれば \(F(x)\) の計算は \(O(1)\) で行えるので、全体で \(O(N + Q)\) でこの問題を解くことができました。

追記

\(f(x) = \min(c_1, \max(b_1, x + a_1))\)\(g(x) = \min(c_2, \max(b_2, x + a_2))\) を合成するより、\(f(x)\) と各操作を合成してみて \(\min(c, \max(b, x + a))\) の形になることを示す方が簡単です。

\(\text{clamp}(x, l, r) = \min(r, \max(l, x))\) と定義すると、

\[ \begin{aligned} F(x) + a &= \text{clamp}(x + (a + c), a + l, a + r)\\ \min(F(x), a) &= \text{clamp}(x + c, \min(a, l), \min(a, r))\\ \max(F(x), a) &= \text{clamp}(x + c, \max(a, l), \max(a, r))\\ \end{aligned} \]

となります。

回答例 (C++)

#include <iostream>
#include <numeric>
#include <algorithm>
using namespace std;
using ll = long long;
const ll INF = numeric_limits<ll>::max() / 4;
void chmin(ll& a, ll b){ if(a > b) a = b; }
void chmax(ll& a, ll b){ if(a < b) a = b; }

int main(){
    ll N;
    cin >> N;
    ll low = -INF, high = INF, add = 0;
    for(ll i = 0; i < N; i++){
        ll A, T;
        cin >> A >> T;
        if(T == 1){
            low += A;
            high += A;
            add += A;
        }
        else if(T == 2){
            chmax(low, A);
            chmax(high, A);
        }
        else{
            chmin(low, A);
            chmin(high, A);
        }
    }
    ll Q;
    cin >> Q;
    for(ll i = 0; i < Q; i++){
        ll x;
        cin >> x;
        cout << clamp(x + add, low, high) << '\n';
    }
}

回答例 (Python)

N = int(input())
low = -1 << 60
high = 1 << 60
add = 0
for i in range(N):
    A, T = map(int, input().split())
    if T == 1:
        low += A
        high += A
        add += A
    elif T == 2:
        if low < A:
            low = A
        if high < A:
            high = A
    else:
        if low > A:
            low = A
        if high > A:
            high = A
input()
for x in map(int, input().split()):
    print(min(high, max(low, x + add)))

posted:
last update: