zoukankan      html  css  js  c++  java
  • 【洛谷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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Android studio 几个坑,值得注意下。
    Android studio使用技巧,不定期更新。
    Android生猛上手,先写个拨号器。
    Ubuntu11.10安装教程,非虚拟机
    在线编辑器CKEditor,多图上传功能实现
    sql 中的NULL小问题 ,大bug
    工资低的.Net程序员,活该你工资低
    30岁的老龄程序员 ,不学习就会被淘汰
    计算商品税额和商品价格保留小数的时候的坑
    大话设计模式--简单工厂模式
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6984.html
Copyright © 2011-2022 走看看