Description
已知一个长度为n的序列a1,a2,...,an。
对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt(abs(i-j))
Input
第一行n,(1<=n<=500000)
下面每行一个整数,其中第i行是ai。(0<=ai<=1000000000)
Output
n行,第i行表示对于i,得到的p
Sample Input
6
5
3
2
4
2
4
5
3
2
4
2
4
Sample Output
2
3
5
3
5
3
5
3
5
4
这么大的数据范围一看就知道和单调性有关。问题是单调性在哪里
将式子转移。用f[i]表示i位置的p最大值
那么有f[i]=max(a[j]+sqrt(abs(i-j)))-a[i]
观察这个式子,可以发现对于一个确定的j,它的转移一定是一个区间[l,r]
那么我们就可以维护转移位置和转移区间,保证队首是最优解
那么当我们加入一个新位置j的时候,和队尾比较。如果它在转移到n的时候都比队尾优,则队尾出队
否则假设队尾的区间是[l,n],那么我们可以在这个区间经行二分查找。找到一个位置p,使得在[l,p-1]队尾元素优,而[p,n]则当前元素优
把队尾区间改成[l,p-1],再把当前元素加入队列
因为转移式中有绝对值。所以我们需要正反跑两遍然后求出个最大的p作为答案即可
#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
double a[500001];
double f[2][500001];
struct save
{
int p;
int l,r;
}q[500001];
inline double cal(int i,int j)
{
return a[i]+sqrt(j-i)-a[j];
}
inline int find(int i,save x)
{
int l=x.l,r=x.r;
while(l<=r)
{
int mid=(l+r)/2;
if(cal(i,mid)>=cal(x.p,mid))
r=mid-1;
else
l=mid+1;
}
return l;
}
int n;
inline void solve(int d)
{
memset(q,0,sizeof(q));
int i;
int r=0,l=1;
for(i=1;i<=n;i++)
{
q[l].l++;
while(l<=r&&q[l].l>q[l].r)
l++;
if(l>r||cal(i,n)>cal(q[r].p,n))
{
while(l<=r&&cal(q[r].p,q[r].l)<cal(i,q[r].l))
r--;
if(l>r)
{
r++;
q[r].p=i;
q[r].l=i;
q[r].r=n;
}
else
{
int rr=find(i,q[r]);
q[r].r=rr-1;
r++;
q[r].p=i;
q[r].l=rr;
q[r].r=n;
}
}
f[d][i]=cal(q[l].p,i);
}
}
inline int gets(double x)
{
if(x-double(int(x))>0)
return int(x)+1;
else
return int(x);
}
int main()
{
scanf("%d",&n);
int i;
for(i=1;i<=n;i++)
scanf("%lf",&a[i]);
solve(0);
for(i=1;i<=n/2;i++)
{
int t=a[i];
a[i]=a[n+1-i];
a[n+1-i]=t;
}
solve(1);
for(i=1;i<=n;i++)
printf("%d\n",gets(max(f[0][i],f[1][n+1-i])));
return 0;
}