2019杭电多校第三场1007 HDU-6609 Find the answer

2019杭电多校第三场1007 HDU-6609 Find the answer

题意:从第一项到第i项的前缀和,如果大于K,问最少删除多少个[1,i]区间内的元素才能小于等于K。因为一共有N个数,所以有N个前缀和,要输出N个值。

PS:最后一个数后面也有空格,这个真的过分

题解: 建一颗空的[1,n]线段树(用来维护当前值的个数总和(sum))。因为第一个数一定符合(a[1]<=K),所以直接输出第一个0,然后把第一个值加进来,单点修改线段树的叶子节点(即:把那个值加入线段树中)。然后第二个值(a[2])先不加入,判断线段树中有多少个尽可能多的值的和能小于等于(K-a[2]),然后将这些小的值的个数减去,就能得到最后需要删除的数的个数。

PS2:线段树中的值是经过离散化再除去重复后再排序的,存在vector < ll > v中,因为数据量太大了,必须离散化。不过到了9102年还有题目这样卡空间,真是惊了我

代码:


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<math.h>
#include<vector>
typedef long long ll;
using namespace std; 
const int MAX = 2e5+7;
vector < ll>v;
struct node
{
	int l, r;
	ll sum;
	ll num;
}tree[MAX*19];
void build(int id, int l, int r) {
	tree[id].l = l;
	tree[id].r = r;
	tree[id].sum = 0;
	tree[id].num = 0;
	int mid = (l + r) / 2;
	if (l == r)
		return ;
	build(id << 1, l, mid);
	build(id << 1 | 1, mid + 1, r);
}
void push_up(int id) {
	tree[id].num = tree[id << 1].num + tree[id << 1 | 1].num;
	tree[id].sum = tree[id << 1].sum + tree[id << 1 | 1].sum;
}
void update(int id, int pos) {
	int l = tree[id].l, r = tree[id].r;
	if (l == r) {
		tree[id].num++;
		tree[id].sum += v[tree[id].l - 1];
	}
	else {
		int mid = (l + r) / 2;
		if (pos > mid) update(id << 1 | 1, pos);
		else update(id << 1, pos);

		push_up(id);
	}
	
}
int query(int id, int s) {
	if (s >= tree[id].sum){
		return tree[id].num;
	}
	else if (tree[id].l == tree[id].r) {
		return s / v[tree[id].l - 1];
	}
	if (tree[id << 1].sum >= s) return query(id << 1, s); //因为经过排序
	//左子树会保存小一些的数值,所以先遍历左子树
	else return tree[id << 1].num + query(id << 1 | 1, s- tree[id<<1].sum);
}
int getid(int x) {  //std的map太慢了,手写一个
	return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
ll a[MAX];
int main() {
	int t;
	cin >> t;	
	while (t--) {
		int n, k;
		cin >> n >> k;
		for (int i = 1; i <= n; i++) {
			scanf("%lld", &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());
		printf("0 ");
		update(1, getid(a[1]));
		for (int i = 2; i <= n; i++) {
			int re = i - query(1, k - a[i]) - 1;
			printf("%d ", re);
			update(1, getid(a[i]));
		}
		printf("\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值