【题目链接】
【前置技能】
- 主席树
【题解】
- 首先先考虑一种时间复杂度为 的暴力做法:将所有数按从小到大排序,考虑做到第 位前缀和为 ,并且前面的数能表示 中的所有数,那么如果 ,则前 个数可以表示 中的所有数,否则,第一个不能被表示的神秘数就是 。
- 那么我们考虑如何加速这个过程:若目前的前缀和为 ,那么我们可以一次性将所有小于等于 的数求和,作为新的 重复上述过程。显然这个过程实质上和上面依次加入的过程是一样的,那么这么做是否要比暴力快呢?发现这么做最坏的情况是斐波那切数列,我们可以认为这是 级别的。
- 那么对于本题,要求区间询问,支持区间求小于等于某个数的和,我们就可以用主席树维护。注意数的范围打到了 ,建主席树的时候可以考虑离散化或者动态开节点。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 100010
#define MAXLOG 30
using namespace std;
int N, w[MAXN], Q;
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
struct Segment_Tree{
struct info{int ls, rs, sum;}a[MAXN * MAXLOG];
int n, cnt, root[MAXN];
void update(int &pos, int old, int l, int r, int p){
pos = ++cnt, a[pos] = a[old], a[pos].sum += p;
if (l == r) return;
int mid = (l + r) >> 1;
if (p <= mid) update(a[pos].ls, a[old].ls, l, mid, p);
else update(a[pos].rs, a[old].rs, mid + 1, r, p);
}
void init(int x){
n = x, cnt = 0;
root[0] = ++cnt;
for (int i = 1; i <= N; ++i)
update(root[i], root[i - 1], 1, n, w[i]);
}
int query(int lp, int rp, int l, int r, int ll, int rr){
if (l == ll && r == rr) return a[rp].sum - a[lp].sum;
int mid = (l + r) >> 1;
if (rr <= mid) return query(a[lp].ls, a[rp].ls, l, mid, ll, rr);
else if (ll > mid) return query(a[lp].rs, a[rp].rs, mid + 1, r, ll, rr);
else return query(a[lp].ls, a[rp].ls, l, mid, ll, mid) + query(a[lp].rs, a[rp].rs, mid + 1, r, mid + 1, rr);
}
int query(int l, int r, int L, int R){
if (R > n) R = n;
return query(root[l - 1], root[r], 1, n, L, R);
}
int work(int l, int r){
int ret = 1;
while (1){
int tmp = query(l, r, 1, ret);
if (tmp >= ret) ret = tmp + 1;
else break;
}
return ret;
}
}sgt;
int main(){
read(N);
int maxn = 0;
for (int i = 1; i <= N; ++i)
read(w[i]), chkmax(maxn, w[i]);
sgt.init(maxn);
read(Q);
while (Q--){
int l, r; read(l), read(r);
printf("%d\n", sgt.work(l, r));
}
return 0;
}