AtCoder Beginner Contest 287 G Balance Update Query

发布时间 2023-06-04 22:13:07作者: zltzlt

洛谷传送门

AtCoder 传送门

线段树上二分入门题。

考虑一个贪心:每次询问按 \(a_i\) 从大到小选。正确性显然。

考虑动态开点线段树,每个结点 \(a_i \in [l, r]\)\(\sum\limits_{a_i \in [l, r]} b_i\)\(\sum\limits_{a_i \in [l, r]} a_i b_i\)。线段树上二分找到第一个 \(\sum\limits_{i = y}^{\infty} b_i > x\)\(p\),那么 \(p + 1 \sim \infty\) 全选,\(p\) 选的数量根据还能选多少数而定。

时间复杂度 \(O((n + q) \log V)\)

code
// Problem: G - Balance Update Query
// Contest: AtCoder - UNIQUE VISION Programming Contest 2023 New Year (AtCoder Beginner Contest 287)
// URL: https://atcoder.jp/contests/abc287/tasks/abc287_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 200100;
const int N = 1000000000;

int n, m, a[maxn], b[maxn];
ll sum[maxn << 7];
int rt, ntot, ls[maxn << 7], rs[maxn << 7], cnt[maxn << 7];

void update(int &rt, int l, int r, int x, ll y) {
	if (!rt) {
		rt = ++ntot;
	}
	cnt[rt] += y;
	sum[rt] += x * y;
	if (l == r) {
		return;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) {
		update(ls[rt], l, mid, x, y);
	} else {
		update(rs[rt], mid + 1, r, x, y);
	}
}

int querycnt(int rt, int l, int r, int ql, int qr) {
	if (ql > qr || !rt) {
		return 0;
	}
	if (ql <= l && r <= qr) {
		return cnt[rt];
	}
	int mid = (l + r) >> 1, res = 0;
	if (ql <= mid) {
		res += querycnt(ls[rt], l, mid, ql, qr);
	}
	if (qr > mid) {
		res += querycnt(rs[rt], mid + 1, r, ql, qr);
	}
	return res;
}

ll querysum(int rt, int l, int r, int ql, int qr) {
	if (ql > qr || !rt) {
		return 0;
	}
	if (ql <= l && r <= qr) {
		return sum[rt];
	}
	int mid = (l + r) >> 1;
	ll res = 0;
	if (ql <= mid) {
		res += querysum(ls[rt], l, mid, ql, qr);
	}
	if (qr > mid) {
		res += querysum(rs[rt], mid + 1, r, ql, qr);
	}
	return res;
}

int find(int rt, int l, int r, int x) {
	if (l == r) {
		return l;
	}
	int mid = (l + r) >> 1;
	if (cnt[rs[rt]] > x) {
		return find(rs[rt], mid + 1, r, x);
	} else {
		return find(ls[rt], l, mid, x - cnt[rs[rt]]);
	}
}

void solve() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &a[i], &b[i]);
		update(rt, 0, N, a[i], b[i]);
	}
	scanf("%d", &m);
	while (m--) {
		int op, x, y;
		scanf("%d%d", &op, &x);
		if (op == 1) {
			scanf("%d", &y);
			update(rt, 0, N, a[x], -b[x]);
			a[x] = y;
			update(rt, 0, N, a[x], b[x]);
		} else if (op == 2) {
			scanf("%d", &y);
			update(rt, 0, N, a[x], y - b[x]);
			b[x] = y;
		} else {
			int t = querycnt(rt, 0, N, 0, N);
			if (t < x) {
				puts("-1");
				continue;
			}
			if (t == x) {
				printf("%lld\n", querysum(rt, 0, N, 0, N));
				continue;
			}
			int pos = find(rt, 0, N, x);
			ll p = querycnt(rt, 0, N, pos + 1, N), q = querysum(rt, 0, N, pos + 1, N);
			printf("%lld\n", q + (x - p) * pos);
		}
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}