题目大意:
给你一个长度为 n n n 的序列,对于每个长度为 m m m 的连续子序列,求出前 k k k 小的数的和。
思路:
考虑将原数组离散化,对于每个长度为 m m m 的区间,需要用 tr1 维护出离散化后的每个下标次数,tr2 表示在离散化下表下原数组的和,每次查询答案,先二分出最后一个小于等于 k k k 的下标 l l l,如果 l = = k l == k l==k ,答案就是 t r 2. s u m ( 0 , k ) tr2.sum(0,k) tr2.sum(0,k), 否则进一步处理得出 l + 1 l+1 l+1 位置的原数组的值,答案加上相应的值即可。
代码:
#include <bits/stdc++.h>
#define int long long
constexpr int N = 2E5 + 10;
using namespace std;
using LL = long long;
int lowbit(int x) { return x & -x; }
int tr[2][N];
int n, m, k;
int sum(int u, int x) {
int res = 0;
while (x) {
res += tr[u][x];
x -= lowbit(x);
}
return res;
}
void add(int u, int x, int c) {
while (x <= n) {
tr[u][x] += c;
x += lowbit(x);
}
}
int query() {
int l = 1, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (sum(0, mid) <= k)
l = mid;
else
r = mid - 1;
}
if (sum(0, l) == k) return sum(1, l);
int dx = k - sum(0, l);
int val = (sum(1, l + 1) - sum(1, l)) / (sum(0, l + 1) - sum(0, l));
return sum(1, l) + dx * val;
}
signed main() {
cin >> n >> m >> k;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
auto b = a;
sort(b.begin(), b.end());
unordered_map<int, int> mp;
int cnt = 0;
mp[b[1]] = ++cnt;
for (int i = 2; i <= n; i++) {
if (b[i] == b[i - 1])
mp[b[i]] = cnt;
else
mp[b[i]] = ++cnt;
}
for (int i = 1; i <= m; i++) {
int p = mp[a[i]];
add(0, p, 1);
add(1, p, a[i]);
}
cout << query() << ' ';
for (int i = m + 1; i <= n; i++) {
int p = mp[a[i - m]];
add(0, p, -1);
add(1, p, -a[i - m]);
p = mp[a[i]];
add(0, p, 1);
add(1, p, a[i]);
cout << query() << ' ';
}
}