qwq
自闭,啥都不会。
首先我们不难发现题目中的要求可以转化成
p
≥
m
a
x
(
a
j
+
(
i
−
j
)
)
−
a
i
p \ge max(a_j+\sqrt {(i-j)}) - a_i
p≥max(aj+(i−j))−ai
那么这个东西怎么做呢?
qwq
貌似同时考虑两个方向的贡献,并不是特别好处理,那不妨我们从前做一遍,从后做一遍,每次只考虑前面的点对于后面的点的贡献,然后两次直接取
m
a
x
max
max就
o
k
ok
ok
那么这个时候,我们考虑
如果存在一个
k
>
j
k>j
k>j
满足
i
>
k
i>k
i>k
且存在 a [ k ] + ( i − k ) > a [ j ] + i − j a[k]+\sqrt{(i-k)} >a[j]+\sqrt{i-j} a[k]+(i−k)>a[j]+i−j的话
由于我们发现 y = x y=\sqrt x y=x这个函数是一个上凸的,也就是说,这个函数的斜率是不断变小的,那么由于k比较大, i − k \sqrt{i-k} i−k的变化率比较大,所以对于 i i i,之后的任意一个位置,都应该满足 a [ k ] + ( i − k ) > a [ j ] + i − j a[k]+\sqrt{(i-k)} >a[j]+\sqrt{i-j} a[k]+(i−k)>a[j]+i−j
所以如果存在这样一个关系,那么我们会发现,从前开始的每一个位置都一个对应的以他作为决策点的区间。
qwq
只要维护好这个,然后用每个点对应的决策点去更新,两个方向取
m
a
x
max
max,就解决这个问题了。
但是我们应该怎么维护呢?
考虑双端队列。
维护一个三元组
[
l
,
r
,
n
u
m
]
[l,r,num]
[l,r,num]
表示这个决策点的编号,和他对应的区间。
首先在第 i i i个元素入队之前,先将 q [ h e a d ] . l + + q[head].l++ q[head].l++,表示每次随着当前点的右移,转移到对应当前点的决策点。
然后弹出不合法的区间,类似 q [ h e a d ] . l > q [ h e a d ] . r q[head].l>q[head].r q[head].l>q[head].r
考虑入队的过程。
如果对于 q [ t a i l ] . l q[tail].l q[tail].l来说,当前的 i i i,比 q [ t a i l ] . n u m q[tail].num q[tail].num更优秀的话,根据上面推的结论,这个队尾元素是没有用的,就可以直接退队,然后把当前元素对应的 l l l更新成 q [ t a i l ] . l q[tail].l q[tail].l,重复这个过程
while (head<=tail && count(q[tail].l,i)>=count(q[tail].l,q[tail].num))
l=q[tail].l,tail--; //如果大于,后面就一直大于
如果对于 q [ t a i l ] . l q[tail].l q[tail].l,当前的 i i i已经不够优秀了,那我们考虑,对于这个队尾元素维护的 [ l , r ] [l,r] [l,r]区间内,一定存在某一个的点,满足这个点之前是队尾元素更优秀,之后是 i i i更优秀。
那么我们通过二分来求解
int solve(int p,int x,int y)
{
int l = p,r=n;
int ans=n+1;
while (l<=r)
{
int mid = l+r >> 1;
if (count(mid,x)>=count(mid,y)) ans = mid,r=mid-1;
else l=mid+1;;
}
return ans;
}
if (head>tail) //找到决策点
{
q[++tail].l=l;
q[tail].r=n;
q[tail].num=i;
}
else
{
int tmp = solve(q[tail].l,i,q[tail].num);//二分那个转折点
if (tmp!=n+1)
{
q[tail].r=tmp-1;
q[++tail].l=tmp;
q[tail].r=n;
q[tail].num=i;
}
}
但是需要注意的是,如果不存在这样一个点,就可以直接忽略当前元素了
经过这一番操作之后,直接用当前位置对应的决策点更新一下,正反做两遍,取 m a x max max就 o k ok ok
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 1e6+1e2;
struct Node{
int l,r,num;
};
Node q[maxn];
int head=1,tail=0;
int a[maxn];
int n,m;
double ans[maxn];
double ans1[maxn];
int b[maxn];
double count(int i,int j)
{
double xx = sqrt(abs(i-j));
return 1.0*a[j]+xx;
}
int solve(int p,int x,int y)
{
int l = p,r=n;
int ans=n+1;
while (l<=r)
{
int mid = l+r >> 1;
if (count(mid,x)>=count(mid,y)) ans = mid,r=mid-1;
else l=mid+1;;
}
return ans;
}
int main()
{
n=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++)
{
q[head].l++;//每次随着当前点的右移,转移到对应当前点的决策点。
while (head<=tail && q[head].l>q[head].r) head++;
int l=1;
while (head<=tail && count(q[tail].l,i)>=count(q[tail].l,q[tail].num)) l=q[tail].l,tail--; //如果大于,后面就一直大于
if (head>tail) //找到决策点
{
q[++tail].l=l;
q[tail].r=n;
q[tail].num=i;
}
else
{
int tmp = solve(q[tail].l,i,q[tail].num);
q[tail].r=tmp-1;
q[++tail].l=tmp;
q[tail].r=n;
q[tail].num=i;
}
ans[i]=count(i,q[head].num)-a[i];
}
head=1,tail=0;
memset(q,0,sizeof(q));
for (int i=1;i<=n;i++) b[i]=a[i];
for (int i=1;i<=n;i++) a[i]=b[n-i+1];
for (int i=1;i<=n;i++)
{
q[head].l++;//每次随着当前点的右移,转移到对应当前点的决策点。
while (head<=tail && q[head].l>q[head].r) head++;
int l=1;
while (head<=tail && count(q[tail].l,i)>=count(q[tail].l,q[tail].num)) l=q[tail].l,tail--; //如果大于,后面就一直大于
if (head>tail) //找到决策点
{
q[++tail].l=l;
q[tail].r=n;
q[tail].num=i;
}
else
{
int tmp = solve(q[tail].l,i,q[tail].num);//二分那个转折点
if (tmp!=n+1)
{
q[tail].r=tmp-1;
q[++tail].l=tmp;
q[tail].r=n;
q[tail].num=i;
}
}
ans1[i]=count(i,q[head].num)-a[i];
}
for (int i=1;i<=n;i++) cout<<(int)ceil(max(ans[i],ans1[n-i+1]))<<"\n";
return 0;
}