H. Ksyusha and the Loaded Set

onlyblues發表於2024-08-16

H. Ksyusha and the Loaded Set

Ksyusha decided to start a game development company. To stand out among competitors and achieve success, she decided to write her own game engine. The engine must support a set initially consisting of $n$ distinct integers $a_1, a_2, \ldots, a_n$.

The set will undergo $m$ operations sequentially. The operations can be of the following types:

  • Insert element $x$ into the set;
  • Remove element $x$ from the set;
  • Report the $k$-load of the set.

The $k$-load of the set is defined as the minimum positive integer $d$ such that the integers $d, d + 1, \ldots, d + (k - 1)$ do not appear in this set. For example, the $3$-load of the set $\{3, 4, 6, 11\}$ is $7$, since the integers $7, 8, 9$ are absent from the set, and no smaller value fits.

Ksyusha is busy with management tasks, so you will have to write the engine. Implement efficient support for the described operations.

Input

The first line contains an integer $t$ ($1 \le t \le 10^4$) — the number of test cases.

The following lines describe the test cases.

The first line contains an integer $n$ ($1 \le n \le 2 \cdot 10^5$) — the initial size of the set.

The second line contains $n$ integers $a_1, a_2, \ldots, a_n$ ($1 \le a_1 < a_2 < \ldots < a_n \le 2 \cdot 10^6$) — the initial state of the set.

The third line contains an integer $m$ ($1 \le m \le 2 \cdot 10^5$) — the number of operations.

The next $m$ lines contain the operations. The operations are given in the following format:

  • + $x$ ($1 \le x \le 2 \cdot 10^6$) — insert element $x$ into the set (it is guaranteed that $x$ is not in the set);
  • - $x$ ($1 \le x \le 2 \cdot 10^6$) — remove element $x$ from the set (it is guaranteed that $x$ is in the set);
  • ? $k$ ($1 \le k \le 2 \cdot 10^6$) — output the value of the $k$-load of the set.

It is guaranteed that the sum of $n$ across all test cases does not exceed $2 \cdot 10^5$, and the same holds for $m$.

Output

For each test case, output the answers to the operations of type "?".

Example

Input

3
5
1 2 5 905 2000000
15
- 2
? 2
? 1
- 1
? 1
+ 4
+ 2
? 2
+ 6
- 4
+ 7
? 2
? 3
? 4
? 2000000
5
3 4 5 6 8
9
? 5
- 5
? 5
+ 1
? 2
- 6
- 8
+ 6
? 5
5
6 7 8 9 10
10
? 5
- 6
? 4
- 10
+ 5
- 8
+ 3
+ 2
- 3
+ 10

Output

2 2 1 6 3 8 8 2000001 
9 9 9 7 
1 1 

解題思路

  和這題 P2894 [USACO08FEB] Hotel G 幾乎一樣。

  把插入 $x$ 看成在下標 $x$ 處出置為 $1$,移除 $x$ 就置為 $0$,詢問就相當於在 $1 \sim 4 \cdot 10^6$ 中查詢長度至少為 $k$ 的連續一段 $0$ 的最小左端點。所有的修改操作都可以用線段樹進行維護,詢問操作則對線段樹進行二分。

  先定義線段樹節點:

struct Node {
    int l, r; // 維護區間的左右端點
    int len;  // 區間長度
    int ls;   // 區間左端點起往右連續一段0的長度
    int rs;   // 區間右端點起往左連續一段0的長度
    int s;    // 整個區間連續一段0的最大長度
}tr[N * 4];

  考慮 pushup 操作,假設要更新的節點 $u$ 的左右兒子分別是 $l$ 和 $r$。顯然有 $u\mathrm{.ls} = l\mathrm{.ls}$,如果左兒子維護的區間全是 $0$,即 $l\mathrm{.ls} = l\mathrm{.len}$,那麼還需要加上 $r\mathrm{.ls}$。同理有 $u\mathrm{.rs} = r\mathrm{.rs}$,如果右兒子維護的區間全是 $0$ 還要加上 $l\mathrm{.rs}$。最後是 $u\mathrm{.s}$,把分成三種情況,整段 $0$ 都在左兒子,該情況最長的一段 $0$ 是 $l\mathrm{.s}$;整段 $0$ 都在右兒子,該情況最長的一段 $0$ 是 $r\mathrm{.s}$;整段 $0$ 橫跨左右兒子,該情況最長的一段 $0$ 是 $l\mathrm{.rs} + r\mathrm{.ls}$。

void pushup(int u) {
    tr[u].ls = tr[u << 1].ls + (tr[u << 1].s == tr[u << 1].len) * tr[u << 1 | 1].ls;
    tr[u].rs = tr[u << 1 | 1].rs + (tr[u << 1 | 1].s == tr[u << 1 | 1].len) * tr[u << 1].rs;
    tr[u].s = max({tr[u << 1].s, tr[u << 1 | 1].s, tr[u << 1].rs + tr[u << 1 | 1].ls});
}

  修改操作就是基本的單點修改,其中如果要將下標 $x$ 置為 $c$,那麼需要把維護該位置的節點的資訊都置為 $\neg c$:

void modify(int u, int x, int c) {
    if (tr[u].l == tr[u].r) {
        tr[u].ls = tr[u].rs = tr[u].s = tr[u].len * !c;
    }
    else {
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) modify(u << 1, x, c);
        else modify(u << 1 | 1, x, c);
        pushup(u);
    }
}

  最後是查詢操作,因為要找到滿足條件的最小的下標,因此先看看當前節點的左兒子 $l$ 是否存在長度至少為 $k$ 的一段 $0$,如果存在則遞迴到 $l$。否則看看橫跨左右兒子的一段 $0$ 的長度是否至少為 $k$,如果滿足則直接回傳 $l\mathrm{.r} - l\mathrm{.rs} + 1$。最後再看看右兒子 $r$ 是否存在長度至少為 $k$ 的一段 $0$,存在則遞迴到 $r$(一定存在,因為維護的值域最大設到了 $4 \cdot 10^6$)。

int query(int u, int k) {
    if (tr[u].l == tr[u].r) return tr[u].l;
    if (tr[u << 1].s >= k) return query(u << 1, k);
    if (tr[u << 1].rs + tr[u << 1 | 1].ls >= k) return tr[u << 1].r - tr[u << 1].rs + 1;
    if (tr[u << 1 | 1].s >= k) return query(u << 1 | 1, k);
    return 0;
}

  AC 程式碼如下,時間複雜度為 $O((n+m) \log{A})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 4e6 + 5;

struct Node {
    int l, r, len, ls, rs, s;
}tr[N * 4];

void pushup(int u) {
    tr[u].ls = tr[u << 1].ls + (tr[u << 1].s == tr[u << 1].len) * tr[u << 1 | 1].ls;
    tr[u].rs = tr[u << 1 | 1].rs + (tr[u << 1 | 1].s == tr[u << 1 | 1].len) * tr[u << 1].rs;
    tr[u].s = max({tr[u << 1].s, tr[u << 1 | 1].s, tr[u << 1].rs + tr[u << 1 | 1].ls});
}

void build(int u, int l, int r) {
    tr[u] = {l, r, r - l + 1};
    if (l == r) {
        tr[u].ls = tr[u].rs = tr[u].s = 1;
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void modify(int u, int x, int c) {
    if (tr[u].l == tr[u].r) {
        tr[u].ls = tr[u].rs = tr[u].s = tr[u].len * !c;
    }
    else {
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) modify(u << 1, x, c);
        else modify(u << 1 | 1, x, c);
        pushup(u);
    }
}

int query(int u, int k) {
    if (tr[u].l == tr[u].r) return tr[u].l;
    if (tr[u << 1].s >= k) return query(u << 1, k);
    if (tr[u << 1].rs + tr[u << 1 | 1].ls >= k) return tr[u << 1].r - tr[u << 1].rs + 1;
    if (tr[u << 1 | 1].s >= k) return query(u << 1 | 1, k);
    return 0;
}

void solve() {
    int n, m;
    cin >> n;
    set<int> st;
    while (n--) {
        int x;
        cin >> x;
        st.insert(x);
        modify(1, x, 1);
    }
    cin >> m;
    while (m--) {
        char op;
        int x;
        cin >> op >> x;
        if (op == '+') {
            st.insert(x);
            modify(1, x, 1);
        }
        else if (op == '-') {
            st.erase(x);
            modify(1, x, 0);
        }
        else {
            cout << query(1, x) << ' ';
        }
    }
    for (auto &x : st) {
        modify(1, x, 0);
    }
    cout << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    build(1, 1, N - 1);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

參考資料

  Codeforces Round 966 (Div. 3) A - H:https://zhuanlan.zhihu.com/p/714360692

相關文章