HDU-6609 Find the answer (权值线段树)

本文介绍了如何解决一个关于序列修改的问题,目标是在不超过给定限制m的情况下,通过改变序列中元素使其前缀和小于等于m,求最小改动次数。通过建立权值线段树并寻找最大权值来找到最佳策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

 

题目:

Given a sequence of n integers called W and an integer m. For each i (1 <= i <= n), you can choose some elements WkWk (1 <= k < i), and change them to zero to make ∑ij=1∑j=1iWjWj<=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*105105 
1 <= m <= 109109 
For each i, 1 <= WiWi <= m

Output

For each test case, you should output n integers in one line: i-th integer means the minimum number of chosen elements WkWk (1 <= k < i), and change them to zero to make ∑ij=1∑j=1iWjWj<=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

题目大意

对于每一个数Ai,在1~i-1删除最少的数,使得SUMi小于m

思路:

先离散化建权值线段树,然后在权值线段树里面求得那个最大的权值就行。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>

using namespace std;
const int maxn = 200005;

int Sum[maxn<<2];
long long totSum[maxn<<2];
int num[maxn];

vector<int>v;

int getid(int value)
{
    return lower_bound(v.begin(),v.end(),value)-v.begin()+1;
}

void update(int pos,int l, int r,int rt)
{
    if(l==r)
    {
        Sum[rt]+=1;
        totSum[rt]+=v[pos-1];
        return ;
    }
    int mid = (l+r)/2;
    if(mid>=pos)update(pos,l,mid,rt<<1);
    else update(pos,mid+1,r,rt<<1|1);
    Sum[rt] = Sum[rt<<1]+Sum[rt<<1|1];
    totSum[rt] = totSum[rt<<1]+totSum[rt<<1|1];
}

int query(int l, int r, int rt, int res)
{
//    cout << l<<" "<<r<<" "<<rt<<" "<<res<<endl;
    if(l==r)
    {
        return res/v[l-1];
    }
    long long sum = totSum[rt<<1];
//    cout << sum<<endl;
    int mid = (l+r)/2;
    if(sum>=res)
        return query(l,mid,rt<<1,res);
    else
        return Sum[rt<<1]+query(mid+1,r,rt<<1|1,res-totSum[rt<<1]);
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        v.clear();

        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &num[i]);
            v.push_back(num[i]);
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        int sz = v.size();
        memset(Sum,0,sizeof(int)*sz*4+2);
        memset(totSum,0,sizeof(long long)*sz*4+2);
        long long tot = 0;
        for(int i = 1; i <= n; i++)
        {
            tot+=num[i];
            if(tot<=(long long)m)
                printf("0 ");
            else
            {
                int ans = query(1,sz,1,m-num[i]);
                printf("%d ", i-ans-1);
            }
            update(getid(num[i]),1,sz,1);
        }
        printf("\n");

    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值