[BZOJ2216|Luogu P3515] [Poi2011]Lightning Conductor (线性解法)
[BZOJ2216|Luogu P3515] [Poi2011]Lightning Conductor (线性解法)
老题了
问题描述:
给定ai,求fi=max
首先可以分j<i,j>i两种情况考虑,下面只考虑j<i的情况
设g_i(x)=\sqrt{x-i}+a_i,比较g_i(x),g_j(x)(i<j)在[j,\infty)上的大小关系时时,有几种情况
-
g_i(x)恒大于g_j(x),即a_i\ge a_j
-
设\delta(x)=g_i(x)-g_j(x),则\displaystyle \delta'(x)=\frac{1}{2\sqrt{x-i}}-\frac{1}{2\sqrt{x-j}}<0
- \delta(0)\leq 0,则g_i(x)恒小于g_j(x)
- 求得\delta(x)的零点,在零点左侧g_i(x)大,右侧g_j(x)大
容易发现这样函数之间的关系类似直线之间的关系,都是在某一界点处大小关系改变
因此可以像维护直线凸包一样维护g_i(x)的凸包,再根据查询的单调性完成线性查询
维护凸包时需要求g_i(x),g_j(x)(i<j)的交点,这个方程大概可以归纳为
\sqrt{x+d}-\sqrt{x}=c
自然可以在整数域上二分求解这个方程的近似根,但是实际上可以用一点点数学手段O(1)求解准确根
令t=\sqrt x,原方程变为\sqrt {y^2+d}-y=c
即y^2+d=y^2+c^2+2cy\Longrightarrow y=\frac{d-c^2}{2c}
故\displaystyle x=(\frac{d-c^2}{2c})^2
计算常数略大,我写得也不是很好,所以不算太快
char buf[200000],*p1,*p2;
#define getchar() (((p1==p2)&&(p2=(p1=buf)+fread(buf,1,200000,stdin))),*p1++)
int rd(){
int s=0; static char c;
while(c=getchar(),c<48);
do s=(s<<1)+(s<<3)+c-48;
while(c=getchar(),c>47);
return s;
}
const int N=5e5+10,INF=1e9+10;
int n;
int a[N],f[N],_sqrt[N];
int Q[N],L,R;
db Cross(int i,int j){
int d=j-i,c=a[j]-a[i];
if(1ll*c*c>=d) return -1e90;
db t=(d-1ll*c*c)/(2.*c);
return t*t+j;
}
int Get(int i,int j){ return _sqrt[j-i]+a[i]-a[j]; }
void Solve(){
L=1,R=0;
rep(i,1,n) {
while(L<R && i>=Cross(Q[L],Q[L+1])) L++;
if(L<=R) cmax(f[i],Get(Q[L],i));
if(L<=R && a[Q[R]]>=a[i]) continue;
while(L<R && Cross(Q[R-1],Q[R])>=Cross(Q[R],i)) R--;
Q[++R]=i;
}
}
void wt(int x){
if(!x) return (void)putchar('0');
static char buf[20];
int l=0;
while(x) buf[++l]=x%10+'0',x/=10;
while(l) putchar(buf[l--]);
}
int main(){
n=rd();
for(int i=1,t=1;i<=n;++i) _sqrt[i]=t+=i>t*t;
rep(i,1,n) a[i]=rd();
Solve();
reverse(a+1,a+n+1),reverse(f+1,f+n+1);
Solve();
drep(i,n,1) wt(f[i]),putchar('\n');
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步