Luogu

分析

先考虑暴力怎么做。把 $[l, r]$ 中的数排序,然后维护 $lim$ 表示 $[1, lim]$ 中的数都能被表示出。假设当前加入了 $w$,那么

  • 如果 $w > lim + 1$,则 $lim + 1$ 不能被表示出,答案即为 $lim + 1$;
  • 否则更新 $lim$ 为 $lim + w$。

考虑优化。设上一次的 $lim$ 为 $pre$,那么只有 $[pre + 1, lim + 1]$ 中的数会影响当前的 $lim$,全部加进去即可。这样子需要支持求区间内 $[l, r]$ 中数的和,主席树即可。

考虑复杂度。可以发现每次 $lim$ 都至少乘 $2$,所以复杂度 $\mathcal{O}(m \log^2 A)$。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
typedef long long ll;

int read() {
    int X = 0, w = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
    while (c >= '0' && c <= '9') X = X * 10 + c - '0', c = getchar();
    return X * w;
}

const int N = 100000 + 10, L = 1e9;

int n, m, a[N];

#define ls(o) ch[o][0]
#define rs(o) ch[o][1]
int rt[N], tot = 0;
int ch[N * 40][2], sum[N * 40];

void modify(int &o, int l, int r, int p, int w) {
    ++tot, ls(tot) = ls(o), rs(tot) = rs(o), sum[tot] = sum[o];
    o = tot, sum[o] += p * w;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (p <= mid) modify(ls(o), l, mid, p, w);
    else modify(rs(o), mid + 1, r, p, w);
}
int query(int x, int y, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return sum[y] - sum[x];
    int mid = (l + r) >> 1, res = 0;
    if (ql <= mid) res += query(ls(x), ls(y), l, mid, ql, qr);
    if (qr > mid) res += query(rs(x), rs(y), mid + 1, r, ql, qr);
    return res;
}
#undef ls
#undef rs

int main() {
    n = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    for (int i = 1; i <= n; ++i)
        rt[i] = rt[i - 1], modify(rt[i], 1, L, a[i], 1);
    m = read();
    while (m--) {
        int l = read(), r = read();
        int lim = 0, pre = 0, s = 0;
        while (1) {
            s = query(rt[l - 1], rt[r], 1, L, pre + 1, lim + 1);
            if (!s) break;
            pre = lim + 1, lim += s;
        }
        printf("%d\n", lim + 1);
    }
    return 0;
}
最后修改:2021 年 03 月 23 日 04 : 49 PM