原题链接.
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. 收获
学到了永远不要放弃治疗,其实你离想出答案只差一步。