[BZOJ2216|Luogu P3515] [Poi2011]Lightning Conductor (线性解法)
[BZOJ2216|Luogu P3515] [Poi2011]Lightning Conductor (线性解法)
老题了
问题描述:
给定\(a_i\),求\(f_i=\max_j\{a_j-a_i+\sqrt{|i-j|}\}\)
首先可以分\(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');
}