题意:给出一组数,对于每个数 a [ i ] a[i] a[i]求出 i i i最少删除多少个数使得他的前缀和小于 m m m。
思路:用权值线段树维护数的个数以及以 i i i为右端点的前缀和,查询就是查最多可以取多少数使前缀和小于 m m m。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10;
int t, a[N], n;
vector<int> v;
ll m;
struct node {
ll num, sum;
} tree[N << 2];
ll getid(ll x) {
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
void update(int l, int r, int rt, int pos) {
if(l == r) {//其实在这里若l==r的话,也满足l==pos
tree[rt].num++;
tree[rt].sum += v[l - 1];
return;
}
int m = (l + r) >> 1;
if(pos <= m)
update(l, m, rt << 1, pos);
else
update(m + 1, r, rt << 1 | 1, pos);
tree[rt].num = tree[rt << 1].num + tree[rt << 1 | 1].num;
tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}
int query(int l, int r, ll k, int rt) {
if(l == r) {
if(k >= v[l - 1])
return k / v[l - 1];
return 0;
}
int m = (l + r) >> 1;
ll Lsum = tree[rt << 1].sum;
if(k <= Lsum)
return query(l, m, k, rt << 1);
else
return tree[rt << 1].num + query(m + 1, r, k - Lsum, rt << 1 | 1);
}
int main() {
cin >> t;
while(t--) {
v.clear();
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> a[i], v.push_back(a[i]);
sort(v.begin(), v.end()), v.erase(unique(v.begin(), v.end()), v.end());
memset(tree, 0, sizeof tree);
ll su = 0;
for(int i = 1; i <= n; i++) {
su += a[i];
if(su > m)
printf("%d ", i - 1 - query(1, v.size(), m - a[i], 1));
else
printf("0 ");
update(1, v.size(), 1, getid(a[i]));
}
puts("");
}
return 0;
}