HDUOJ 6609 Find the answer

HDUOJ 6609 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 <= 2e5
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

权值线段树的应用,非常巧妙的一道题,我讲一下解题思路~
首先肯定要求前缀和 s u m [ i ] sum[i] sum[i],那么每次删掉的数总和必须大于 s u m [ i ] − m sum[i]-m sum[i]m,那么可以考虑用权值线段树,两个变量,一个记录总和,一个记录出现的次数~

  • 更新操作很简单,记录总和的数组加上对应位置的值,出现的次数加一即可
  • 难点在于查询,我们查询的目的就是求最少的元素个数使得元素之和大于等于 x = s u m [ i ] − m x=sum[i]-m x=sum[i]m,其实就相当于在线段树上不断对 x = s u m [ i ] − m x=sum[i]-m x=sum[i]m 操作直至其变为 0 0 0,因为要最小元素个数,所以一定优先删除右子树的元素,那么对某一过程的 x x x,我们就判断其和右子树的总和的大小,如果大于总和,就令 x x x 减去 t r e e [ 2 ∗ i + 1 ] tree[2*i+1] tree[2i+1],同时答案加上右子duoshan树元素的个数 c n t [ 2 ∗ i + 1 ] cnt[2*i+1] cnt[2i+1];如果小于,那么直接进入右子树即可;如果查询到单点,注意次时的单点一定是最小元素,如果不能整除,试想删除这个单点后 x x x 还有剩余,所以要多删一个,也就是必须向上取整

权值线段树的两个主要操作就在上面,难点也就是这些,如果对线段树还是难以理解,建议自己在草稿纸上建树模拟一下样例,很快就能理解了,AC代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int t,n,k,ans[N],cnt[N<<2];
ll m,a[N],sum=0,tree[N<<2];
vector<ll>v;
int getid(ll x){
    
    
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}

void pushup(int i)
{
    
    
    tree[i]=tree[i<<1]+tree[i<<1|1];
    cnt[i]=cnt[i<<1]+cnt[i<<1|1];
}

void build(int i,int l,int r)
{
    
    
    if(l==r)
    {
    
    
        tree[i]=cnt[i]=0;
        return ;
    }
    int mid=l+r>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    pushup(i);
}

void update(int i,int l,int r,int x)
{
    
    
    if(l==r)
    {
    
    
        tree[i]+=v[l-1];
        cnt[i]++;
        return ;
    }
    int mid=l+r>>1;
    if(x<=mid) update(i<<1,l,mid,x);
    else update(i<<1|1,mid+1,r,x);
    pushup(i);
}

int query(int i,int l,int r,ll x)
{
    
    
    if(l==r) return (x+v[l-1]-1)/v[l-1];
    int mid=l+r>>1,ans=0;
    if(x<tree[i<<1|1]) ans+=query(i<<1|1,mid+1,r,x);
    else ans+=cnt[i<<1|1]+query(i<<1,l,mid,x-tree[i<<1|1]);
    return ans;
}

int main(){
    
    
    scanf("%d",&t);
    while(t--){
    
    
        v.clear();
        sum=0;
        scanf("%d%lld",&n,&m);
        for(int i=1;i<=n;i++){
    
    
            scanf("%lld",&a[i]);
            v.push_back(a[i]);
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        k=v.size();
        build(1,1,k);
        for(int i=1;i<=n;i++){
    
    
            sum+=a[i];
            if(i>1) ans[i]=query(1,1,k,sum-m);
            update(1,1,k,getid(a[i]));
        }
        for(int i=1;i<=n;i++){
    
    
            printf("%d ",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43765333/article/details/108651636
今日推荐