【洛谷6984】[NEERC2015] Landscape Improved(二分)

点此看题面

  • 给定一个长度为(n)的序列(a)
  • 你最多进行(lim)次操作,每次可以从([2,n-1])中选择一个满足(a_{i-1}ge a_i)(a_{i+1}ge a_i)的位置,给(a_i)(1)
  • (max_{i=1}^na_i)的最大值。
  • (nle10^5,limle10^{18})

二分答案

容易想到去二分答案(x)

考虑检验,对于一个位置(i),想让(a_i)取到(x),最节省的方法就是让两侧分别依次填入(x-1,x-2,...)

如果这一过程中某一个位置的数本来就大于等于我们想要填入的数,那么它及其之后的位置就都不用再填了。

以左侧为例,相当于是要找到最大的位置(j)满足(a_jge x-(i-j)Leftrightarrow a_j-jge x-i)

所以实际上我们还可以对于每个(i)再次二分(j)的位置,每次利用(RMQ)求出(midsim i)(a_j-j)的最大值。

而右侧其实也是类似的,就是要找到最小的位置(j)满足(a_j+jge x+i),同理可以二分并用(RMQ)求最大值。

然后要计算步数,直接用这一部分我们希望填入的数的总和减去原有的(a_j)的总和即可。

代码:(O(nlogn))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define LN 17
#define LL long long
using namespace std;
int n,a[N+5];LL lim,s[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
struct RMQ
{
	int LG[N+5],Mx[N+5][LN+1];I void Init()//预处理
	{
		RI i,j;for(i=2;i<=n;++i) LG[i]=LG[i>>1]+1;
		for(j=1;(1<<j)<=n;++j) for(i=1;i+(1<<j)-1<=n;++i) Mx[i][j]=max(Mx[i][j-1],Mx[i+(1<<j-1)][j-1]);
	}
	I int Q(CI x,CI y) {RI k=LG[y-x+1];return max(Mx[x][k],Mx[y-(1<<k)+1][k]);}//询问区间最大值
}A,B;
I bool Check(CI i,Con LL& x)//检验a[i]能否取到x
{
	RI l,r,mid;LL t;
	l=1,r=i;W(l^r) mid=l+r-1>>1,x-i>A.Q(mid,i)?r=mid:l=mid+1;//二分左边界
	if(r==1) return 0;if(1.0L*(x-i+r+x)*(i-r+1)/2-(s[i]-s[r-1])>lim) return 0;//计算,注意a[1]不能修改
	t=(x-i+r+x)*(i-r+1)/2-(s[i]-s[r-1]);
	l=i,r=n;W(l^r) mid=l+r+1>>1,x+i>B.Q(i,mid)?l=mid:r=mid-1;//二分右边界
	if(l==n) return 0;if(1.0L*(x+i-l+x-1)*(l-i)/2-(s[l]-s[i])>lim) return 0;//计算,注意a[n]不能修改
	return t+(x+i-l+x-1)*(l-i)/2-(s[l]-s[i])<=lim;
}
int main()
{
	RI i;for(read(n,lim),i=1;i<=n;++i)
		read(a[i]),s[i]=s[i-1]+a[i],A.Mx[i][0]=a[i]-i,B.Mx[i][0]=a[i]+i;//统计前缀和,记录RMQ对象
	LL l,r,mid,ans=max(a[1],a[n]);for(A.Init(),B.Init(),i=2;i<n;++i)//枚举每个位置
		{l=a[i],r=a[i]+lim;W(l<r) mid=l+r+1>>1,Check(i,mid)?l=mid:r=mid-1;ans=max(ans,l);}//二分答案
	return printf("%lld
",ans),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6984.html