# BJTUOJ 一颗姜会长多高? (二分+前缀和+简单贪心)

原题链接.

1.题意

给你一堆高度为h[i]的姜,现有2种操作,可操作个数分别为m1,m2。操作1:某个姜高度增加1,操作2:某个姜高度直接变成某个特定值。(当然m2<n)。
现在给你t个询问,每个询问给你m1,m2,问你在此条件下最矮的姜的最大值为多少?

2.思路

首先,操作2可以任意增加高度,可以贪心地把最小的m2棵姜施加操作2,从而只讨论对剩下的n-m2棵施加操作1。那么问题就转换成,对于剩下的姜使用操作1,最矮的最高为多少?
其次,我们先考虑二分答案ans。那么咋写check函数?这需要先找到最后一个高度小于等于ans的姜j,然后判断m1是否小于等于(j-m2)*ans-(sum[j]-sum[m2]),这其实又是一个二分!那么时间复杂度实际为log1e18 * log1e5 * O ( M ) O(M) O(M),这样实际上会被卡!
转换思路,发现姜的高度如果被补成相同高度后,就可以用 O1的复杂度求出这些姜最矮的高度。于是考虑二分剩下的姜,找到最大的下标j,使得把m2+1到j这些姜补齐到a[j]的高度所需的操作数刚好小于等于m1,我们可以发现,答案其实就是a[j]+(m1-price)/(j-m2)!其中,price是把这些姜补齐到a[j]所需操作数。

然后需要注意一下:开cincout加速会TLE,卡了我好久,c++必须用scanf(当然也可能是没有把endl换成’\n’的问题)。
以及,万能头文件用时会整整慢上一秒,这是把编译时间也算进去了吗??

3.代码

#include<cstdio>
#include <algorithm>
//#include <bits/stdc++.h>


using namespace std;

typedef long long LL;
const int N=1e5+10;
const LL M=1e16;

int n,m;
int a[N];
LL sum[N];
LL mx,my;


bool check(LL x)
{
    LL tar=(x-my)*a[x-1]-sum[x]+sum[my];//目标值
    return tar<=mx;
}

LL bisearch()
{
    LL l=my+1,r=(LL)n;//my<n
    while(l<r)
    {
        LL mid=(l+r+1)>>1;
        if(check(mid))l=mid;
        else r=mid-1;
    }
    LL res=a[l-1]+(mx-((l-my)*a[l-1]-sum[l]+sum[my]))/(l-my);
    return res;
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    sort(a,a+n);
    for(int i=0;i<n;i++)sum[i+1]=sum[i]+(LL)a[i];
    while(m--)
    {
        scanf("%lld %lld",&mx,&my)printf("%lld\n",bisearch());
    }
}

4. 收获

学到了永远不要放弃治疗,其实你离想出答案只差一步。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值