题面在这里
description
已知一个长度为(n)的序列(a_1,a_2,...,a_n)。
对于每个(1le ile n),找到最小的非负整数(p),
满足对于任意的(1le jle n),(a_jle a_i+p-sqrt{|i-j|})
data range
solution
绝对值怎么办?
我们先从左到右(DP j< i)的部分(此时有(|i-j|=i-j)),
再右到左(DP j> i)的部分(此时有(|i-j|=j-i));
那么我们现在考虑这个式子:
对于每一个相同的(i),
记(w[j]=sqrt{i-j}),那么我们很容易发现这个函数是凸函数
对于两个决策(j<k),当((j-k))一定时,我们发现(w[j]-w[k])随着(i)的递增而递减
而对于这两个决策,(j)比(k)优当且仅当对于(j<k<ile n),
即
当(i)递增时,(g[j,k])这个函数单调递减,那么存在一个分界点(i'),
使得当(ile i')时,(g[j,k]ge 0)((j)更优),当(ige i')时,(g[j,k]le 0)((k)更优);
那么我们知道这个(DP)方程具有决策单调性
具体如何实现?
考虑相邻的两个决策(j,k),
由于上面的性质,我们可以二分出这两个决策的分界点(t[j,k]);
对于相邻的三个决策(x,y,z),如果(t[x,y]ge t[y,z])那么决策(y)是没有用的可以扔掉
考虑维护一个单调队列,存放所有合法的决策;
由于我们在维护的同时(i)是变化的,
因此我们需要求出每个决策此时转移最优的区间
即单调队列存放的元素为((pos,l_{pos},r_{pos}))(注意(pos < l_{pos}le r_{pos}))
当加入新决策(j)时,考虑队尾的决策((k,l_k,r_k));
如果(j)总是比(k)要劣(在这里指对于(i=n)仍有(a[j]+sqrt{i-j}le a[k]+sqrt{i-k})),
则不考虑决策(j);
否则(此时(r_j=n)),如果对于(i=l_k)有(a[j]+sqrt{i-j}ge a[k]+sqrt{i-k}),
一直弹掉队尾决策并考虑队尾的前一个决策,如果队列为空则压入((j,j,n));
否则(此时存在一个决策(k)无法被弹出),考虑二分决策(k)和(j)的分界点(mid)
(本人的定义是,对于(ile mid),有(a[k]+sqrt{i-k}> a[j]+sqrt{i-j})),
之后将(r_k)修改为(mid),压入决策((j,mid,n))。
il dd calc(int j,int i){return a[j]-a[i]+sqrt(i-j);}
//计算j转移至i时对应的DP值
il int divide(int k,int j,int l,int r){
RG int mid,ans=l;
while(l<=r){
mid=(l+r)>>1;
if(calc(k,mid)>calc(j,mid))
ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
//二分分界点
il void push(int i){
if(calc(Qx[R],n)>calc(i,n))return;
while(calc(Qx[R],Ql[R])<calc(i,Ql[R])&&L<=R)R--;
if(L>R){Ql[++R]=i;Qr[R]=n;Qx[R]=i;return;}
Qr[R]=divide(Qx[R],i,Ql[R],Qr[R]);
R++;Ql[R]=Qr[R-1]+1;Qr[R]=n;Qx[R]=i;
}
//插入新决策
当要查询位置(pos)时,直接从队头开始查找,
如果队首决策((k,l_k,r_k))中(r_k<pos)或队首决策劣于队首后决策则将其弹出;
否则,将其作为此次查询的答案
il int pop(int i){
while(Qr[L]<i||(L<R&&calc(Qx[L],i)<calc(Qx[L+1],i)))L++;
return Qx[L];
}
//查询答案
这里是总的(DP)模板
il void DP(int *a,int *p,int n){
L=0;R=-1;
for(RG int i=1;i<=n;i++){
if(i!=1)p[i]=ceil(calc(pop(i),i));Ql[L]=i;push(i);
//答案为非负整数,这里做了上取整
}
}
//DP序列
code
#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int N=1000000;
const int inf=1e9+7;
il ll read(){
RG ll data=0,w=1;RG char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
return data*w;
}
il void file(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
}
int n,a[N],p[N],s[N];
int Qx[N],Ql[N],Qr[N],L,R;
il dd calc(int j,int i){return a[j]-a[i]+sqrt(i-j);}
//计算j转移至i时对应的DP值
il int divide(int k,int j,int l,int r){
RG int mid,ans=l;
while(l<=r){
mid=(l+r)>>1;
if(calc(k,mid)>calc(j,mid))
ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
//二分分界点
il void push(int i){
if(calc(Qx[R],n)>calc(i,n))return;
while(calc(Qx[R],Ql[R])<calc(i,Ql[R])&&L<=R)R--;
if(L>R){Ql[++R]=i;Qr[R]=n;Qx[R]=i;return;}
Qr[R]=divide(Qx[R],i,Ql[R],Qr[R]);
R++;Ql[R]=Qr[R-1]+1;Qr[R]=n;Qx[R]=i;
}
//插入新决策
il int pop(int i){
while(Qr[L]<i||(L<R&&calc(Qx[L],i)<calc(Qx[L+1],i)))L++;
return Qx[L];
}
//查询答案
il void DP(int *a,int *p,int n){
L=0;R=-1;
for(RG int i=1;i<=n;i++){
if(i!=1)p[i]=ceil(calc(pop(i),i));Ql[L]=i;push(i);
}
}
//DP序列
int main()
{
n=read();
for(RG int i=1;i<=n;i++)a[i]=read();
DP(a,p,n);reverse(a+1,a+1+n);DP(a,s,n);
for(RG int i=1;i<=n;i++)
printf("%d
",max(0,max(p[i],s[n-i+1])));
//记得答案是非负整数
return 0;
}