HDU - 6609 Find the answer(线段树)

本文深入探讨了线段树算法的应用,特别是在处理一系列整数序列的动态更新与查询问题上。通过实例,详细讲解了如何利用线段树来优化求解最小元素选择问题,以确保序列的累积和不超过给定阈值。文章涵盖了线段树的构建、更新及查询操作,为读者提供了全面的算法理解和实践指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Given a sequence of n integers called W and an integer m. For each i (1 <= i <= n), you can choose some elements WkWk (1 <= k < i), and change them to zero to make ∑ij=1∑j=1iWjWj<=m. So what's the minimum number of chosen elements to meet the requirements above?.

Input

The first line contains an integer Q --- the number of test cases.
For each test case:
The first line contains two integers n and m --- n represents the number of elemens in sequence W and m is as described above.
The second line contains n integers, which means the sequence W.

1 <= Q <= 15
1 <= n <= 2*105105
1 <= m <= 109109
For each i, 1 <= WiWi <= m

Output

For each test case, you should output n integers in one line: i-th integer means the minimum number of chosen elements WkWk (1 <= k < i), and change them to zero to make ∑ij=1∑j=1iWjWj<=m.

Sample Input

2  
7 15  
1 2 3 4 5 6 7  
5 100  
80 40 40 40 60

Sample Output

0 0 0 0 0 2 3  
0 1 1 2 3

 

             用一个线段树维护前i-1数的一个子集,保证在数量最多的情况下,总和小于m。这样对于第i个数,如果这个数加上后总和小于m,那就放进这个子集;如果放进去之后超过了,而且这个数比这个子集里面最大数要小,那么放进这个数,去除最大数就好了;如果放进去之后超过而且还比最大数大,那么这个数在后面一定被抛弃,那么直接判断最少用多少数能把她放进去就行了。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 200005;
ll sum[maxn * 4], ma[maxn * 4], num[maxn * 4];
ll sot[maxn], tmp[maxn];
ll n, m;
void pushup(int rt) {
	sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
	ma[rt] = max(ma[rt << 1], ma[rt << 1 | 1]);
	num[rt] = num[rt << 1] + num[rt << 1 | 1];
}
void build(int l, int r, int rt) {
	sum[rt] = ma[rt] = num[rt] = 0;
	if (l == r)return;
	int m = (l + r) >> 1;
	build(lson); build(rson);
}
void add(int l, int r, int rt, int x, int k) {
	//cout << l << " " << r << " " << rt << " " << x << "\n";
	if (l == r) {
		sum[rt] += sot[x] * k;
		num[rt] += k;
		if (num[rt] != 0) ma[rt] = sot[l];
		else ma[rt] = 0;
		return;
	}
	int m = (l + r) >> 1;
	if (x <= m)add(lson, x, k);
	else add(rson, x, k);
	pushup(rt);
}
ll query(int l, int r, int rt, ll su) {
//	cout << l << " " << r << " " << rt << " " << su << "\n";
	if (su <= 0)return 0;
	if (l == r) {
		ll t = ((su - 1) / sot[l]) + 1;
		if (t < 0)t = 0;
		return t;
	}
	int m = (l + r) >> 1;
	ll res = 0;
	if (sum[rt << 1 | 1] >= su) {
		res += query(rson, su);
	}
	else {
		res += num[rt << 1 | 1] + query(lson, su - sum[rt << 1 | 1]);
	}
	return res;
}
int id(ll x) {
	return lower_bound(sot + 1, sot + 1 + n, x) - sot;
}
int main() {
	int te;
	scanf("%d", &te);
	while (te--) {
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++) {
			scanf("%lld", &sot[i]);
			tmp[i] = sot[i];
		}
		sort(sot + 1, sot + n + 1);
		build(1, n, 1);
		ll ans = 0;
		for (int i = 1; i <= n; i++) {
			ll big = ma[1], asum = sum[1];
			if (asum + tmp[i] <= m) {
				add(1, n, 1, id(tmp[i]), 1);
				printf("%lld", ans); printf(" ");
				continue;
			}
			if (big >= tmp[i]) {
				add(1, n, 1, id(big), -1);
				add(1, n, 1, id(tmp[i]), 1);
				ans++;
				printf("%lld", ans); printf(" ");
				continue;
			}
			ll tm = ans;
			tm += query(1, n, 1, asum + tmp[i] - m); 
			printf("%lld", tm); printf(" ");
			ans++;
		}
		printf("\n");
	}
	return 0;
}
/*
5
5 3
1 2 1 2 1
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值