题目
我们很快就能想到贪心的思想:选行李轻的同学。
可是用暴力会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;
}