Official

F - Erase between X and Y Editorial by yuto1115

解説

連結リストに似たアルゴリズムを用います。

具体的には、現在の \(A\) の中に現れるそれぞれの値 \(i\) について、\(A\) の中で \(i\) の直後に現れる値(\(i\) が末尾にあるならば \(-1\))を \(\text{next}_i\) と定義し、これを管理することを考えます。

はじめ、\(A\) の中に現れる値は \(0\) のみであり、\(\text{next}_0=-1\) です。

\(1\) 種類目のクエリ、すなわち値 \(x\) の直後に値 \(i\) を挿入する操作について考えます。\(i\) を挿入する前の \(\text{next}_x\) の値を \(y\) とおくと、元々 \(\dots\rightarrow x\rightarrow y\rightarrow \dots\) となっていた所が \(\dots\rightarrow x\rightarrow i\rightarrow y\rightarrow \dots\) というように変化するわけですから、\(\text{next}_x\) の値が \(i\) に、\(\text{next}_i\) の値が \(y\) にそれぞれ更新され、逆にそれ以外の更新はありません。

\(2\) 種類目のクエリ、すなわち値 \(x\) と値 \(y\) の間にある値の合計を出力し、それらを削除するという操作について考えます。

まず、\(A\) の中で \(x\)\(y\) よりも先に現れると分かっている場合について考えます。この場合、\(x\) から始めて \(x\rightarrow \text{next}_x \rightarrow \text{next}_{\text{next}_x} \rightarrow \dots\) というように辿っていき、\(y\) に到達するまで続ければ、\(x\)\(y\) の間にある要素が全てわかります。その後これらの要素を削除するためには、単に \(\text{next}_x\) の値を \(y\) に更新すればよいです。削除される要素の数に比例する計算量がかかりますが、要素の追加が高々 \(Q\) 回しか起こらないことから、削除される要素の数も合計で高々 \(Q\) 個に抑えられます。

\(A\) の中で \(y\)\(x\) よりも先に現れると分かっている場合も、同様に \(y\) から始めて \(y\rightarrow \text{next}_y \rightarrow \text{next}_{\text{next}_y} \rightarrow \dots\) と辿っていけばよいです。

問題は、実際には \(A\) の中で \(x\)\(y\) のどちらが先に現れるのか分からないことです。しかし、\(x\rightarrow \text{next}_x \rightarrow \text{next}_{\text{next}_x} \rightarrow \dots\) と辿っていく過程と \(y\rightarrow \text{next}_y \rightarrow \text{next}_{\text{next}_y} \rightarrow \dots\) と辿っていく過程を \(1\) ステップずつ交互に進めていき、前者が \(y\) に到達した or 後者が \(x\) に到達したタイミングで打ち切れば、削除される要素数の \(2\) 倍程度のステップ数で済むことになり、全体の計算量を変わらず \(O(Q)\) に抑えることができます。

実装例 (C++) :

#include <bits/stdc++.h>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int q;
    cin >> q;
    vector<int> next(q + 1);
    next[0] = -1;
    for (int i = 1; i <= q; i++) {
        int t;
        cin >> t;
        if (t == 1) {
            int x;
            cin >> x;
            next[i] = next[x];
            next[x] = i;
        } else {
            int x, y;
            cin >> x >> y;
            int cx = x, cy = y;
            long long sx = 0, sy = 0;
            while (true) {
                if (next[cx] != -1) {
                    cx = next[cx];
                    if (cx == y) {
                        cout << sx << '\n';
                        next[x] = y;
                        break;
                    } else {
                        sx += cx;
                    }
                }
                if (next[cy] != -1) {
                    cy = next[cy];
                    if (cy == x) {
                        cout << sy << '\n';
                        next[y] = x;
                        break;
                    } else {
                        sy += cy;
                    }
                }
            }
        }
    }
}

posted:
last update: