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;
}

原文地址:https://www.cnblogs.com/zjl192628928/p/11270094.html

时间: 2024-10-21 13:14:51

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

hdu 5775 Bubble Sort(2016 Multi-University Training Contest 4——树状数组)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5775 Bubble Sort Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 636    Accepted Submission(s): 378 Problem Description P is a permutation of the

AtCoder Regular Contest 075 E - Meaningful Mean 树状数组求顺序对, 前缀和

题目链接: http://arc075.contest.atcoder.jp/tasks/arc075_c 题意: 给你一个序列和一个数k,求有多少对l,r,使得a[l]+a[l+1]+...+a[r]的算术平均数大于等于k 1≤N≤2×10^5 1≤K≤10^9 1≤ai≤10^9 思路: 首先对于所有数减去k,这样就不用除(r-l+1), 然后我们发现所求的就是有多少对l,r,使得sum[r]-sum[l-1] >= 0, sum是减去k之后的序列的前缀和 用树状数组对sum求有多少个顺序对

query 2019徐州网络赛(树状数组)

query \[ Time Limit: 2000 ms \quad Memory Limit: 262144 kB \] 题意 补题才发现比赛的时候读了一个假题意.... 给出长度为 \(n\) 的排列,在给出 \(m\) 次询问,每次询问有一对 \(l.r\),问满足 \(min(ai, aj) = gcd(ai, aj)\) 的 \(pair(i, j)\) 对数. 思路 考虑离线做 先把每个数出现的位置记录下来,然后预处理出所有的 \(pair\). 对于一个数字 \(x\),其满足条件

hdu 5792 World is Exploding(2016 Multi-University Training Contest 5——树状数组)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5792 World is Exploding Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 643    Accepted Submission(s): 306 Problem Description Given a sequence A

2019 HDOJ Multi-University Training Contest Stage 8(杭电多校)

中规中矩的一场. 题目链接:http://acm.hdu.edu.cn/contests/contest_show.php?cid=855 C: 定义函数f(d,k)为数字d在数字k中出现的次数.给定d和x,找到尽量大的k使得k<=x且f(d,k)==k. 很诡异的一题,最好的做法仍然是打表找规律.题解给了一个很神奇的结论:满足条件的k<1011且k的分布非常稀疏. 1 /* basic header */ 2 #include <bits/stdc++.h> 3 /* defin

2019 HDOJ Multi-University Training Contest Stage 10(杭电多校)

最后一场多校打得一般般. 题目链接:http://acm.hdu.edu.cn/contests/contest_show.php?cid=857 C: E: I: BFS水题. 1 /* Codeforces Contest 2019_mutc_10 2 * Problem I 3 * Au: SJoshua 4 */ 5 #include <queue> 6 #include <cstdio> 7 #include <vector> 8 #include <s

2019 HDOJ Multi-University Training Contest Stage 2(杭电多校)

服务器时不时爆炸,有点难受. 题目链接:http://acm.hdu.edu.cn/userloginex.php?cid=849 A: 神仙题.不可做题. B: dp. C: 推式子题. D: 边分治. E: 可以数学推理的题.但是显然打表更快找出规律.对打出来的结果做两次差分即可. 1 /* basic header */ 2 #include <bits/stdc++.h> 3 /* define */ 4 #define ll long long 5 #define dou doubl

2019 HDOJ Multi-University Training Contest Stage 4(杭电多校)

很抱歉过了这么多天才补这场,最近真的挺忙的…… 出题人是朝鲜的(目测是金策工业?),挺难. 题目链接:http://acm.hdu.edu.cn/contests/contest_show.php?cid=851 A: 签到题. 对于当前的点,若其编号为偶数,则可与1相连使得边权贡献为0.否则从低位向高位找当前点编号的二进制表示的第一个0,使这个0变为1,其他位置变为0并检查新的数字是否小于等于n.若小于等于n则贡献为0,反之贡献为1. 1 /* basic header */ 2 #inclu

hdu 6665 Calabash and Landlord (2019 Multi-University Training Contest 8 1009)(离散化)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6665 题目大意:给定四个点分别为两个矩形的左上角和右下角,两个矩形与坐标轴平行,问坐标平面(含负轴,但矩形只在第一象限内,部分边可能在正轴上)被两个矩形划分为几个区域(原意问的是几个连通点集但因为是实数点所以相当于问的是区域). 思路:如题,离散化.可以看到点坐标的值可能非常大,但是注意求的只是区域个数,而并不用求每个区域内点的个数,所以我们想要的信息只有点和点之间的位置关系,这时候最简单的方法,就