杭电2019多校赛第三场 HDU 6609 Find the answer(二分答案+树状数组)

下午这题又用优先队列写TLE,又二分TLE,想到可以线段树但是时间不够没有写。。。自闭的一下午~

题目传送门

HDU 6609 Find the answer(2019HDU多校赛第三场1007)

Time Limit: 4000MS Memory Limit: 64MB

Description

Given a sequence of n integers called W and an integer m. For each i (1 <= i <= n), you can choose some elements W k W_k Wk (1 <= k < i), and change them to zero to make ∑ j = 1 i \sum_{j=1}^i j=1i W j W_j Wj<=m. So what’s the minimum number of chosen elements to meet the requirements above?

Standard 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 elements 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* 1 0 5 10^5 105

1 <= m <= 1 0 9 10^9 109

For each i, 1 <= W i W_i Wi <= m

Standard Output

For each test case, you should output n integers in one line: i-th integer means the minimum number of chosen elements W k W_k Wk (1 <= k < i), and change them to zero to make ∑ j = 1 i \sum_{j=1}^i j=1i W j W_j Wj<=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

Thought:

二分答案+树状数组

要使删的数尽可能少,就要删尽可能大的数,即加更多小的数。

开始时先记录下数组的原始坐标,然后从小往大排序,用id[i]数组记录初始数列的第i个数排在第几位。每循环一次,就把这个数放进排好序后的位置的树状数组里,同时更新另一个树状数组(表示某个位置是否放入了数),每次二分答案求一下第一个树状数组的前缀和,使其满足题意;再利用第二个树状数组求出这段前缀和数的个数后就能计算出答案了。

代码如下:

Code:

#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
#define LL long long
using namespace std;
const int maxn = 2*1e5+100;
LL id[maxn],num[maxn];
LL s[maxn],no[maxn];
struct node{
    int pos,val;
}a[maxn];
int T,n,m;
inline LL read(){
    LL x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
bool cmp(node x,node y){
    if (x.val==y.val) return x.pos<y.pos;
    return x.val<y.val;
}
LL sum(LL *p,int x){
    LL su=0;
    for (;x>0;x-=lowbit(x)) su+=p[x];
    return su;
}
void add(LL* p,int x,LL num){ 
    for (;x<n;x+=lowbit(x)) p[x]+=num;
}
int main(){
    T=read();
    while (T--){
        n=read();m=read();
        memset(s,0,sizeof s);
        memset(no,0,sizeof no);
        for (int i=1;i<=n;i++){
            num[i]=read();
            a[i].val=num[i];
            a[i].pos=i;
        }
        sort(a+1,a+1+n,cmp);
        for (int i=1;i<=n;i++) id[a[i].pos]=i;
        for (int i=1;i<=n;i++){
            int now=m-num[i];
            int l=0,r=n,pl;
            while (l<=r){
                int mid=(l+r)>>1;
                long long sx=sum(s,mid); 
                if (sx<=now){
                    l=mid+1;pl=mid; 
                }else r=mid-1;
            }
            int cnt=sum(no,pl);
            cout<<i-cnt-1<<" ";
            add(no,id[i],1);
            add(s,id[i],num[i]);
        }
        cout<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值