放假回家题解

题目

我们很快就能想到贪心的思想:选行李轻的同学。

可是用暴力会T(优先队列也是),所以我们想到用树状数组:将行李轻的同学放在前面(没有改变顺序),再通过二分来判断最多选多少个同学

实现过程:(建议结合代码)
设当前要处理的同学是第i位(原顺序),排序(行李重量轻的排在前面)后在第j位,行李重量为w;
则:选择的其他同学的行李重量和 <= m - w;
1.二分,找出最后一个前面同学行李重量和小于m - w的位置,记作last
2.通过树状数组求出last前有多少位同学
3.在树状数组的j位加入一个w

#include <cstdio>
#include <iostream>
#include <algorithm> 
#define LL long long
using namespace std;

const int MAXN = 1e5 + 5; 
int t, n, m;
int a[MAXN], index[MAXN];
//a记录原顺序,index[i]记录第i位同学是第几小(行李重量)的 

LL BITW[MAXN], BITP[MAXN];
//BITW维护行李重量的前缀和,BITP维护人数的前缀和 

struct node {
	int index, val;
}rev[MAXN]; 

void clean ();//清空 
void _Update (int, int);//单点修改 

LL _Sumw (int);//求出行李重量的前缀和 
int _Sumpeople (int);//求出人数的前缀和 
int lowbit (int x) {return x & -x;}

bool check (int, LL); 
bool cmp (node x, node y) {return x.val < y.val;}

int main() {
	cin >> t;
	while (t--) {
		cin >> n >> m;
		for (int i = 1; i <= n; i++) {
			cin >> a[i];
			rev[i].index = i, rev[i].val = a[i];
		}
		
		sort (rev + 1, rev + 1 + n, cmp);
		
		for (int i = 1; i <= n; i++)
			index[rev[i].index] = i;
		//对index进行赋值(即找到a数组对应的排序后的序号) 
		
		for (int i = 1; i <= n; i++) {
			LL _max = m - a[i];
			int l = 0, r = n, last;
			while (l + 1 < r) {
				int mid = (l + r) >> 1;
				if (check (mid, _max)) l = mid;
				else r = mid;
			}
			if (check (r, _max)) last = r;
			else last = l;
			//二分找到最后的位置,满足重量的前缀和小于_max 
			int num = _Sumpeople (last);
			//求出人数 
			cout << i - num - 1 << " ";
			_Update (index[i], a[i]);
			//修改第index[i]位 
		}
		cout << endl;
		clean ();//清空 
	}
	return 0;
}

void clean () {
	for (int i = 1; i <= n; i++) BITP[i] = 0, BITW[i] = 0;
}

void _Update (int x, int val) {
	for (int i = x; i <= n; i += lowbit (i))
		BITW[i] += val, BITP[i]++;
}

int _Sumpeople (int x) {
	int res = 0;
	for (int i = x; i >= 1; i -= lowbit (i)) {
		res += BITP[i];
	}
	return res;
}

LL _Sumw (int x) {
	LL res = 0;
	for (int i = x; i >= 1; i -= lowbit (i)) {
		res += BITW[i];
	}
	return res;
}

bool check (int x, LL _max) {
	LL val = _Sumw (x);
	if (val <= _max) return 1;
	else return 0;
}

由于作者水平有限,没讲清楚的地方请私信我orz

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值