题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6609
题目大意
给你一个n个数的序列,一个数m,问你对于每一个数i,你需要在i前面将最少多少个数置为0,才使得从1到i-1的前缀和不大于m
思路
原本以为直接用两个优先队列,一个大顶堆,保存所有还没有变为0的数;一个小顶堆,保存所有已经变为0的数;
但是这样会TLE,因为你有一个出堆进堆的操作,加入之前给你很多很多个1,然后突然给你一个m,进堆出队堆的次数会承受不了。
所以只能使用权值线段树
线段树节点维护两个东西,区间和,数字出现的次数
因为是权值线段树,如果某个数出现一次,Insert的时候就寻找对应的叶子节点,出现次数++,和+=val
那么我们需要查询什么呢?
当我们访问到i时,之前的i-1个数都已经被插入,那么我们只需要查询在前i-1个数中,最多能选择多少个数,他们的和sum<=m-a[i]
然后i减去查询结果就是答案
(记得离散化)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define int long long
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn];
vector<int>v;
//离散化
int getid(int cur) { return lower_bound(v.begin(), v.end(), cur) - v.begin() + 1; }
//权值线段树
struct Node {
int l, r;
int sum;//当前节点的值
int cnt;//有多少个数
}Tree[maxn<<2];
void Build(int root, int l, int r) {
Tree[root].l = l, Tree[root].r = r;
Tree[root].sum = Tree[root].cnt = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
Build(root << 1, l, mid);
Build(root << 1 | 1, mid + 1, r);
}
void push_up(int root) {
Tree[root].sum = Tree[root << 1].sum + Tree[root << 1 | 1].sum;
Tree[root].cnt = Tree[root << 1].cnt + Tree[root << 1 | 1].cnt;
}
void Insert(int root, int pos) {
if (Tree[root].l == Tree[root].r) {
Tree[root].sum += v[pos - 1];
Tree[root].cnt++;
return;
}
int mid = (Tree[root].l+Tree[root].r) >> 1;
if (pos <= mid) {
Insert(root << 1, pos);
}
else {
Insert(root << 1 | 1, pos);
}
push_up(root);
}
//查询最多可以选择多少个数,使得和不大于当前的m-now
int query(int root,int val) {
if (Tree[root].l == Tree[root].r) {
//只有一个节点,也就是一个值
return min(val / v[Tree[root].l - 1], Tree[root].cnt);
}
int ans = 0;
//左半边和大于当前值,肯定只能拿左半边
if (Tree[root << 1].sum>=val) {
ans += query(root << 1, val );
}
else {//左半边和小于当前值,全拿,再在右半边查询
ans += Tree[root << 1].cnt;
ans += query(root << 1 | 1, val - Tree[root << 1].sum);
}
return ans;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
v.clear();
int n, m;
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());
Build(1, 1, v.size());
cout << "0 ";//第一个数
//第n个数是不用插进来的
for (int i = 1; i < n; i++) {
Insert(1, getid(a[i]));
int ans = query(1, m - a[i+1]);
cout << i - ans << " ";
}
cout << endl;
}
return 0;
}