Gym - 102770E(主席树 求区间k大和)

发现 dp[i][k]=∑ij=1j2+ { b1...bi 中的前 k 大和 }

写一个值域主席树,支持查询区间前 k 大和即可。

const int maxn = 1e5 + 10;
int a[maxn], b[maxn];
int T[maxn], c[maxn * 30], lson[maxn * 30], rson[maxn * 30];
ll sum[maxn * 30], f[maxn];
int tot, t, r, l, k, n, m, q;

int build(int l, int r) {
	int rt = tot++;
	sum[rt] = 0;
	c[rt] = 0;
	if (l != r) {
		int mid = (l + r) >> 1;
		lson[rt] = build(l, mid);
		rson[rt] = build(mid + 1, r);
	}
	return rt;
}

int update(int pre, int l, int r, int pos, int val) {
	int rt = tot++;
	lson[rt] = lson[pre];
	rson[rt] = rson[pre];
	c[rt] = c[pre] + val;
	sum[rt] = sum[pre] + b[pos];
	if (l != r) {
		int mid = (l + r) >> 1;
		if (pos <= mid)	lson[rt] = update(lson[pre], l, mid, pos, val);
		else	rson[rt] = update(rson[pre], mid + 1, r, pos, val);
	}
	return rt;
}

ll query(int lr, int rr, int l, int r, int k) {
	if (l == r)	return b[l] * k;
	int rsum = c[rson[rr]] - c[rson[lr]];
	int mid = (l + r) >> 1;
	if (rsum >= k)
		return query(rson[lr], rson[rr], mid + 1, r, k);
	else
		return sum[rson[rr]] - sum[rson[lr]] + query(lson[lr], lson[rr], l, mid, k - rsum);
}

int main() {
	for (int i = 1; i <= 100000; ++i)	f[i] = (ll)i * i + f[i - 1];
	t = rd();
	while (t--) {
		tot = 0;
		n = rd();
		for (int i = 1; i <= n; ++i) {
			a[i] = rd(); b[i] = a[i];
		}
		sort(b + 1, b + 1 + n);
		m = unique(b + 1, b + 1 + n) - b - 1;
		T[0] = build(1, m);
		for (int i = 1; i <= n; ++i) {
			int pos = lower_bound(b + 1, b + 1 + m, a[i]) - b;
			T[i] = update(T[i - 1], 1, m, pos, 1);
		}
		q = rd();
		while (q--) {
			l = rd(); r = rd(); k = rd();
			write(query(T[l - 1], T[r], 1, m, k) + f[r - l + 1]);
			puts("");
		}
	}
	return 0;
}

posted @ 2020-10-26 20:56  wansheking  阅读(546)  评论(0)    收藏  举报