题目链接
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 Output
0 0 0 0 0 2 3
0 1 1 2 3
- 吐槽
疯狂pe,一个ans后面加一个空格? - 题意
略 - 思路
对于第i个位置,怎样选择数字才会使满足条件情况下选择数字数目最少呢?很容易想到,需要选择前i1个数中较大的数字,使其变为0 基于这个思想,如果我们对于每个位置i都暴力去找最大的前几个数,显然会TLE! 可以注意到,题目可以转化为前i-1个数中最多选出多少个数字和W[i]相加使得其和小于等于m(很容易 想到,选择较小的数才会使选的数最多)//这段抄的题解因为我也是这么想的
我们对给定数组进行离散化,对于离散化之后的数组建立一颗线段树,线段树上的每个节点记录区间之 和以及区间内数字个数。 时间复杂度:Nlog(N)//这是题解的做法
但这样搞代码量有点大啊,下面给一种不要建树的方法
维护两个单调的数组,q[maxn],sum[maxn],q代表前i-1个数中的数的单调递增的序列,sum代表q数组的前缀和,且sum<=m。(也就是说只维护前k个和小于m的数),对每个w,二分查找sum中最后一个小于等于m-w的数,等到答案,在再判断需不需要插入w,需要的话二分插入。有人说这是假算法。分析下,其实这并不是假算法,对于sum数组,考虑数组的最大长度,当数组的最后一个值为m时无法再增长,且要使每次插入w都从第一个插入,所以q内的数各不相同,故数组最大长度为 1 e 9 \sqrt{1e9} 1e9为32000左右.事实上不会每次插入达到这个长度,从0开始,故最多的计算次数大约为3e9,题目给的时限为4秒,我们认为1秒的计算量为1e9显然卡不掉。 - 代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,n,m,cnt,w;
int q[200005]={0};
int sum[200005];
int fd(int l,int r,int x,int *s){
while(l<r){
int mid=(l+r+1)>>1;
if(s[mid] <= x) l=mid;
else r=mid-1;
}
return l;
}
int main(){
scanf("%d",&t);
while(t--){
memset(sum,0,sizeof(sum));
cnt=1;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d",&w);
int dt=m-w;
if(sum[cnt-1]<=dt){
int j=fd(0,cnt-1,w,q);
for(int k=cnt;k>j+1;k--){
q[k]=q[k-1];
sum[k]=sum[k-1]+w;
}
cnt++;
q[j+1]=w;
sum[j+1]=sum[j]+w;
printf("%d ",i-cnt+2);
}else{
int j=fd(0,cnt-1,dt,sum);
printf("%d ",i-j);
if(q[cnt-1]>w){
int j=fd(0,cnt-1,w,q);
for(int k=cnt;k>j+1;k--){
q[k]=q[k-1];
sum[k]=sum[k-1]+w;
}
q[j+1]=w;
sum[j+1]=sum[j]+w;
}
}
}
printf("\n");
}
}
与题解对比我的时间还少了一倍(哈哈,应该是数据的问题)