2019ICPC徐州 H.Yuuki and a problem
阿新 • • 發佈:2020-11-04
給一個序列
要支援兩個操作
- 修改 \(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\n", last + 1); } } } }