Find the Answer
Problem Description
Given a sequence of n integers called W and an integer m. For each i (1 <= i <= n), you can choose some elements Wk (1 <= k < i), and change them to zero to make ∑ij=1Wj<=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*105
1 <= m <= 109
For each i, 1 <= Wi <= m
Output
For each test case, you should output n integers in one line: i-th integer means the minimum number of chosen elements Wk (1 <= k < i), and change them to zero to make ∑ij=1Wj<=m.
Sample Input
2
7 15
1 2 3 4 5 6 7
5 100
80 40 40 40 60
Sample
0 0 0 0 0 2 3
0 1 1 2 3
Brief Description
给定n个数,和一个数字m
为了使前i个数(包括wi)的和小于等于m,我们可以将前i个数(不包括wi)的若干个置0。
问对于n各数中的每个数wi,至少置0多少个数才能满足上述条件
样例分析
5 100
80 40 40 40 60
对于第一个80, 小于m(100) 因此是0个
第二个40,80+40 > m,不满足条件,因此必须在前面删除一些,因为只有一个,所以最少删除一个,输出1
第三个40,80+40+40=160>m,同理,必须在前面删除一些,如果删除的是40,那么剩下的80+40仍然大于100,仍需多删一个,为了删除的最少,只删除80一个, 故输出1
同理。。。。。。
最终输出 0 1 1 2 3
Solution
很容易想到,要删除的少,肯定要先删除数值大的
问题即转化为 求一个k,使得删除前k大后,剩余的和<=m
依题意,wi(当前值)是不可以删的,只可以删除前面的
问题又可以转化为求最小的temp个数,使得他们的和+wi<=m
那么最终k就等于 i-temp
我们的目标就放在了求temp值
怎么求呢?可以尝试二分搜索temp值
对于当前搜索值curr,如果前curr小个数的和+w[i]<=m,即满足条件
Example
例如下图:当前处理的为黄色4=6
后面灰色的暂未处理,蓝色的为已经处理
可以轻易看出 最多 前2小 + 6(即:1+4+6 <=m)
因此答案为 3-2 = 1 (最少删除1个【5】)
如何编程实现此思路
接下来,我们的目标又转移到求区间前curr小个数的和。
如果暴力求和,肯定是会TLE的。因此可以想到用某种数据结构。
树状数组或线段树。
先将数据离散化,
例如 80 40 40 40 60 离散成 5 1 2 3 4
用两个树状数组 一个维护当前已有的i个数的区间和 ,一个维护区间的数的个数
这两个树状数组访问举例说明:
当处理到第三个数(2)时, 对区间【1,4】求和表示: 第一小+到第4小 但是由于当前已有数只有 5 1 2, 没有第3小和第四小,所以求和结果为 1+2=3
另一个树状数组区间中数的个数,比如访问区间【1,4】 得到2
**树状数组在这道题上的运用 **
例如右图:当前处理的为黄色4=6
后面灰色的暂未处理,蓝色的为已经处理
对第一个树状数组,最多访问 query【1,4】 + 6 <=m
对于刚刚得到的最大区间,访问第二个树状数组【1,4】 得知【1,4】有2个
故输出 3-2=1 (3个蓝色;最多可前2小求和)
Data Structure
AC Code:
/*
* Copyright (c) 2019 Ng Kimbing, HNU, All rights reserved. May not be used, modified, or copied without permission.
* @Author: Ng Kimbing, HNU.
* @LastModified:2019-07-29 T 23:00:44.448 +08:00
*/
package ACMProblems.DataStructureProblem.TreeArray;
import java.util.Arrays;
import java.util.Comparator;
import static ACMProblems.ACMIO.*;
public class FindTheAnswer {
private static final int maxN = 200005;
private static Integer[] data = new Integer[maxN];
private static int[] w = new int[maxN];
private static int[] indexOfV = new int[maxN];
private static int[] ans = new int[maxN];
private static int n;
private static void discrete(int n) {
for (int i = 1; i <= n; i++)
data[i] = i;
Arrays.sort(data, 1, n + 1, Comparator.comparingInt(o -> w[o]));
for (int i = 1; i <= n; i++)
indexOfV[data[i]] = i;
}
static class TreeArray {
long[] treeArray;
TreeArray() {
treeArray = new long[maxN];
}
private int lowBit(int x) {
return x & (-x);
}
private void add(int treeIndex, int value) {
while (treeIndex <= n) {
treeArray[treeIndex] += value;
treeIndex += lowBit(treeIndex);
}
}
private long sum(int x) {
long ans = 0;
while (x != 0) {
ans += treeArray[x];
x -= lowBit(x);
}
return ans;
}
private long query(int l, int r) {
return sum(r) - sum(l - 1);
}
private void clear() {
Arrays.fill(treeArray, 0);
}
}
public static void main(String[] args) throws Exception {
int caseNum;
TreeArray sumTree = new TreeArray();
TreeArray countTree = new TreeArray();
for (caseNum = nextInt(); caseNum-- != 0; ) {
sumTree.clear();
countTree.clear();
n = nextInt();
int m = nextInt();
for (int i = 1; i <= n; ++i) {
w[i] = nextInt();
indexOfV[i] = w[i];
}
discrete(n);
ans[1] = 0;
sumTree.add(indexOfV[1], w[1]);
countTree.add(indexOfV[1], 1);
for (int i = 2; i <= n; i++) {
int l = 1, r = n;
//找到尽可能多的前r小的数,使得v[i] + sum(1, r) <= m
while (l <= r) {
int mid = (l + r) >> 1;
if (sumTree.query(1, mid) + w[i] <= m)
l = mid + 1;
else r = mid - 1;
}
//countTree.query(1,r)代表前r小的数在当前区间有多少个
ans[i] = (int) (i - 1 - countTree.query(1, r));
sumTree.add(indexOfV[i], w[i]);
countTree.add(indexOfV[i], 1);
}
for (int i = 1; i <= n; ++i)
out.print(ans[i] + " ");
out.println();
}
out.flush();
}
}