原题面
题目描述
定义一个可重复数字集合\(S\)的神秘数为最小的不能被\(S\)的子集的元素和表示的数
例如:\(S = \{1,1,1,4,13\}\)
则:\(1=1 \\ 2 = 1 + 1 \\ 3 = 1 + 1 + 1 \\ 4 = 4 \\ 5 = 1 + 4 \\ 6 = 1 + 1 + 4 \\ 7 = 1 + 1 + 1 + 4\)
8不能被\(S\)的子集和表示,所以\(S\)的神秘数是8
现有一个长为\(n\)的序列,\(m\)次询问,每次询问输入\(l,r\),求\(S=\{a_i | l \le i \le r \}\)的神秘数
解析
先考虑一个询问怎么处理
将\(l\)到\(r\)的数排序后考虑依次加入每一个数
假设当前可以凑出\([1, t)\)的数,那么计算\(sum = \sum_{a_i \le t} {a_i}\)
如果\(sum < t\),显然\(t\)就是答案
如果\(sum \ge t\),说明凑出\([1, t)\)后一定剩下一个小于等于\(t\)的数,那么\([1, sum]\)的数也可以凑出来,\(t\)移动到\(sum + 1\)
然后考虑多组询问,我们要快速查询\([l, r]\)内小于等于\(t\)的\(a_i\)之和,就可以套主席树了
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#define MAXN 100005
#define MAXA 1000000000
typedef long long LL;
struct ChairmanTree {
struct Node {
Node *ls, *rs;
int sum;
} * root[MAXN];
void build();
void build(Node *, Node *, int, int, int);
int query(Node *, Node *, int, int, int, int);
} T;
int N, M, ans;
int a[MAXN];
char gc();
LL read();
void print(LL);
int main() {
N = read();
for (int i = 1; i <= N; ++i) a[i] = read();
T.build();
M = read();
while (M--) {
int l = read() - 1, r = read();
ans = 1;
while (1) {
int s = T.query(T.root[l], T.root[r], 1, MAXA, 1, ans);
if (s >= ans)
ans = s + 1;
else
break;
}
print(ans);
putchar('\n');
}
return 0;
}
void ChairmanTree::build() {
for (int i = 1; i <= N; ++i) build(root[i - 1], root[i] = new Node(), 1, MAXA, a[i]);
}
void ChairmanTree::build(Node *pre, Node *cur, int L, int R, int v) {
if (L == R)
cur->sum = (pre ? pre->sum : 0) + v;
else {
int mid = (L + R) >> 1;
if (v <= mid) {
if (pre) cur->rs = pre->rs;
build(pre ? pre->ls : NULL, cur->ls = new Node(), L, mid, v);
} else {
if (pre) cur->ls = pre->ls;
build(pre ? pre->rs : NULL, cur->rs = new Node(), mid + 1, R, v);
}
cur->sum = 0;
if (cur->ls) cur->sum += cur->ls->sum;
if (cur->rs) cur->sum += cur->rs->sum;
}
}
int ChairmanTree::query(Node *pre, Node *cur, int L, int R, int l, int r) {
if (!cur) return 0;
if (L >= l && R <= r) return cur->sum - (pre ? pre->sum : 0);
int mid = (L + R) >> 1, res = 0;
if (l <= mid) res += query(pre ? pre->ls : NULL, cur->ls, L, mid, l, r);
if (r > mid) res += query(pre ? pre->rs : NULL, cur->rs, mid + 1, R, l, r);
return res;
}
inline char gc() {
static char buf[1000000], *p1, *p2;
if (p1 == p2) p1 = (p2 = buf) + fread(buf, 1, 1000000, stdin);
return p1 == p2 ? EOF : *p2++;
}
inline LL read() {
LL res = 0;
char ch = gc();
while (ch < '0' || ch > '9') ch = gc();
while (ch >= '0' && ch <= '9') res = (res << 1) + (res << 3) + ch - '0', ch = gc();
return res;
}
inline void print(LL x) {
static int buf[30];
if (!x)
putchar('0');
else {
while (x) buf[++buf[0]] = x % 10, x /= 10;
while (buf[0]) putchar('0' + buf[buf[0]--]);
}
}
//Rhein_E