2019 Multi-University Training Contest 3 Find the answer (离散化+二分+树状数组)

Posted zjl192628928

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019 Multi-University Training Contest 3 Find the answer (离散化+二分+树状数组)相关的知识,希望对你有一定的参考价值。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6609

题目大意:给定一个含有n个数的序列,还有一个m,对于每个i(1<=i<=n)求出最少需要将前i-1个数中的多少个数改成0,才能使得前i个数的和小于m

解题思路:很容易想到,我们应该将比较大的数变为0,答案才是最优的。所以我们直接给他们排个序离散化一下,对应它们在树上的编号,树上节点存两个东西,一个是节点的权值之和,还有一个就是包含数的个数,每次查询时,先将前i-1个数更新到树上,可以保证后面的数不影响答案,假设前i个数的和sum-m=x,则我们只要二分找到个一个最大的l,使得区间[l,n]大于等于x就可以了,这样答案就是区间[l,n]的数的个数了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
int n,m,ans[maxn];
struct node
    int val,id,rk;
a[maxn];
bool cmp1(node x,node y)
    return x.val<y.val;

bool cmp2(node x,node y)
    return x.id<y.id;

ll sum[maxn],num[maxn];
int Ans;
int lowbit(int x)
    return x&(-x);

void add(int x,ll val)
    while(x<=n)
        num[x]++;
        sum[x]+=val;
        x+=lowbit(x);
    

ll ask(int x)
    ll res=0;
    while(x)
        Ans+=num[x];
        res+=sum[x];
        x-=lowbit(x); 
    
    return res;

int main()
    int t;
    scanf("%d",&t);
    while(t--)
        memset(num,0,sizeof(num));
        memset(sum,0,sizeof(sum));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i].val);
            a[i].id=i;
        
        sort(a+1,a+1+n,cmp1);
        for(int i=1;i<=n;i++)
            a[i].rk=i;
        sort(a+1,a+1+n,cmp2);
        ll tmp=0;
        for(int i=1;i<=n;i++)
            tmp+=a[i].val;
            if(tmp<=m)ans[i]=0;
            else
                ll x=tmp-m;
                int l=0,r=n;
                Ans=0;
                ll s1=ask(n); ll Ans1=Ans;
                while(l<=r)
                    int mid=l+r>>1;
                    Ans=0;
                    ll s2=ask(mid); ll Ans2=Ans;
                    if(s1-s2>=x)
                        ans[i]=Ans1-Ans2;
                        l=mid+1; 
                    else r=mid-1;
                
            
        //    cout<<a[i].rk<<" "<<a[i].val<<endl;
            add(a[i].rk,a[i].val);
        
        for(int i=1;i<=n;i++)
            printf("%d ",ans[i]);
        printf("\n");
    
    return 0;

 

以上是关于2019 Multi-University Training Contest 3 Find the answer (离散化+二分+树状数组)的主要内容,如果未能解决你的问题,请参考以下文章

2019 Multi-University Training Contest 6

2019 Multi-University Training Contest 6

2019 Multi-University Training Contest 6

2019 Multi-University Training Contest 3

2019 Multi-University Training Contest 2

2019 Multi-University Training Contest 2