hdu6609 Find the answer (权值线段树)

Find the answer

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

题意:

给个长度为n的数组,对于每个i,你可以选择1~i-1中任意多的数并将它删去(把权值变为0),使得(a[1]-a[i])的和小于等于m,问对于每个i最少删几个数可以达到要求

分析:

删除的最少个数=i-1-保留的最多个数
显然优先保留数值较小的数
因此建立权值线段树,维护每个数的个数与总和
题目变为求最多的数使得他们的和小于等于m-a[i],(从小到大选取)

这题还需要离散化

code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxm=2e5+5;
int a[maxm];
int xx[maxm],cnt;
int n,m;
ll val[maxm<<2];
int num[maxm<<2];
void build(int l,int r,int node){
    val[node]=num[node]=0;
    if(l==r)return ;
    int mid=(l+r)/2;
    build(l,mid,node*2);
    build(mid+1,r,node*2+1);
}
void pushup(int node){
    val[node]=val[node*2]+val[node*2+1];
    num[node]=num[node*2]+num[node*2+1];
}
void update(int x,int l,int r,int node){
    if(l==r){
        val[node]+=xx[x];
        num[node]++;
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid)update(x,l,mid,node*2);
    else update(x,mid+1,r,node*2+1);
    pushup(node);
}
int ask(int x,int l,int r,int node){
    if(val[node]<=x)return num[node];
    if(l==r){//输出需要的个数和拥有的个数中的较小值
        return min(num[node],x/xx[l]);//xx[l]是当前节点未离散化的值
    }
    int mid=(l+r)/2;
    if(val[node*2]>=x)return ask(x,l,mid,node*2);
    else return num[node*2]+ask(x-val[node*2],mid+1,r,node*2+1);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            xx[i]=a[i];
        }
        sort(xx+1,xx+1+n);
        cnt=unique(xx+1,xx+1+n)-(xx+1);
        build(1,cnt,1);
        for(int i=1;i<=n;i++){
            printf("%d ",i-1-ask(m-a[i],1,cnt,1));
            int p=lower_bound(xx+1,xx+1+cnt,a[i])-xx;
            update(p,1,cnt,1);
        }
        puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值