题解
给出一个长度为n的序列a,你需要对于每个i,找到最小的整数p满足对于 ∀ j , a j ≤ a i + p − ∣ i − j ∣ \forall j,a_j\leq a_i+p- \sqrt{|i-j|} ∀j,aj≤ai+p−∣i−j∣
题意
移项,有
p
≥
a
[
j
]
−
a
[
i
]
+
∣
i
−
j
∣
p\geq a[j]-a[i]+\sqrt{|i-j|}
p≥a[j]−a[i]+∣i−j∣
也即是说
p
=
m
a
x
(
a
[
j
]
+
∣
i
−
j
∣
)
−
a
[
i
]
p=max(a[j]+\sqrt{|i-j|})-a[i]
p=max(a[j]+∣i−j∣)−a[i]
如果我们正序倒序都做一遍的话,那么就是
p
=
m
a
x
(
a
[
j
]
+
i
−
j
)
−
a
[
i
]
p=max(a[j]+\sqrt{i-j})-a[i]
p=max(a[j]+i−j)−a[i]
于是奇奇怪怪的东西出现了,决策单调性
观察
i
−
j
\sqrt{i-j}
i−j,由于这个本身的增长率是逐渐变小的(可以求导得到),那么也就是说,当转移i的时候,若j优于k,且k<j,那么后面j一定也优于k
也就是说,每个位置作为最优所覆盖的区间一定是连续的
所以我们就可以用一个单调队列来实现,更新那些位置作为最优所覆盖的区间
显然插入一个新点后,它作为最优所替代的区间是[x,n](当然,也可能是空集),我们就一个个弹掉队尾里整体都比它劣的,最后再在队尾里二分
然后倒过来再做一次就行了。记得dp数组也要倒过来。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=500005;
int n;
int a[N];
int q[N],head,tail;
int L[N],R[N];
int d[N];
double calc(int x,int y){
return a[x]-a[y]+sqrt(abs(x-y));
}
void solve(){
head=1,tail=0;
for(int i=1;i<=n;i++){
while(head<=tail&&R[q[head]]<i)
head++;
if(head<=tail){
L[q[head]]=i;
d[i]=max(d[i],(int)ceil(calc(q[head],i)));
}
if(head>tail||calc(i,n)>calc(q[tail],n)){
R[i]=n;
while(head<=tail&&calc(i,L[q[tail]])>=calc(q[tail],L[q[tail]]))
tail--;
if(head<=tail){
int l=L[q[tail]],r=R[q[tail]]+1;
while(l<r){
int mid=(l+r)>>1;
if(calc(i,mid)<calc(q[tail],mid))
l=mid+1;
else
r=mid;
}
L[i]=l;
R[q[tail]]=l-1;
}
}
else
L[i]=i+1;
q[++tail]=i;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
solve();
reverse(a+1,a+n+1);
reverse(d+1,d+n+1);
solve();
reverse(d+1,d+n+1);
for(int i=1;i<=n;i++)
printf("%d\n",d[i]);
}