1. 程式人生 > 實用技巧 >2019ICPC徐州 H.Yuuki and a problem

2019ICPC徐州 H.Yuuki and a problem

給一個序列

要支援兩個操作

  1. 修改 \(A[x] = y\)
  2. 區間詢問 \([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);
			}
		}
	}
}