给一个序列
要支持两个操作
- 修改 (A[x] = y)
- 区间询问 ([L,R]) 中最小的不能被表示为 ([L,R]) 中子集合 ((subset)) 的和 正整数
首先要明白的几件事情是
首先用一个桶装每个值的和
- 如果没有 (1) 的话,答案就是 (1)
- 若现在能凑成 ([1,sum]) ,如果再来一个 (sum+1) ,就可以凑成 ([1,2sum+1])
- 最开始有 (x) 个 (1) 的话,可以凑成 ([1,x]) ,我们计算 (X = sum_{i=1}^{x+1}sum_i) ,
若数字 (x) 出现 (k) 次 , 则 (sum_x = kx) 。 若 (X = x) ,则答案就是 (x+1)
这样迭代,速度很快
然后数据结构上,用树状数组套一个权值线段树就可以。
树状数组维护了前缀和信息,可以进行区间内的查询。
#include<bits/stdc++.h>
typedef long long ll;
#define mid (l+r>>1)
#define min(a,b) (a<b?a:b)
using namespace std;
const int N = 2e5 + 10;
ll sum[N * 100];int Lc[N * 100], Rc[N * 100], tot;
void insert(int& rt, int l, int r, int val, int pos) {
if (!rt)rt = ++tot;
sum[rt] += val;
if (l == r)return;
if (pos <= mid)insert(Lc[rt], l, mid, val, pos);
else insert(Rc[rt], mid + 1, r, val, pos);
}
ll query(int rt, int l, int r, int L, int R) {
if (!rt) return 0;
if (L <= l and r <= R)return sum[rt];
ll ans = 0;
if (L <= mid)ans += query(Lc[rt], l, mid, L, R);
if (R > mid) ans += query(Rc[rt], mid + 1, r, L, R);
return ans;
}
int lowbit(int x) { return x & (-x); }
int root[N];
const int M = N - 1;
void insert(int pos, int ppos, int val) {
for (int i = pos; i <= M; i += lowbit(i)) {
insert(root[i], 1, M, val, ppos);
}
}
ll query(int l, int r, int L, int R) {
ll ans = 0;
for (int i = r; i; i -= lowbit(i)) {
ans += query(root[i], 1, M, L, R);
}
for (int i = l - 1; i; i -= lowbit(i)) {
ans -= query(root[i], 1, M, L, R);
}
return ans;
}
int n, q;
int A[N];
signed main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", A + i);
insert(i, A[i], A[i]);
}
while (q--) {
int op, a, b; scanf("%d%d%d", &op, &a, &b);
if (op == 1) {
insert(a, A[a], -A[a]);
A[a] = b;
insert(a, A[a], A[a]);
}
else {
ll sum = query(a, b, 1, 1);
if (!sum) puts("1");
else {
ll last = sum;
while (1) {
sum = query(a, b, 1, min(last + 1, M));
if (sum == last)break;
last = sum;
}
printf("%lld
", last + 1);
}
}
}
}