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;
}