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
Sample Output
2
3
5
3
5
4
Sol
首先我们可以推出来:
\(f[i]=max(a_j+\sqrt{i-j})-a_i(j<i)\)
\(f[i]=max(a_j+\sqrt{j-i})-a_i(j>i)\)
然后我们可以分两次dp。
但是暴力dp是\(O(n^2)\)的,会TLE,我们发现这题满足决策单调性的性质。具体地,如果k这个位置的决策是j,那么对于任意\(i<j\),i就不会再产生任何贡献,因为根号的增长会越来越慢,j的增长会比i还要快。而且每个点的贡献区域一定是一个区间,这样的话我们可以进行整体二分,我们每次找到一个区间mid的决策位置pos(直接暴力找),然后根据\([l,mid)\)的决策区域是\([L,pos]\),\((mid,r]\)的决策区域是\([pos,R]\)继续进行求解,注意大小写,小写是处理区间,大写是可决策区间。
代码实现非常容易:
Code
#include <bits/stdc++.h>
using namespace std;
int id[500005],a[500005],n;double f[500005],ans[500005];
double get(int x,int y){return a[x]+f[abs(y-x)];}
void solve(int l,int r,int L,int R)
{
if(l>r) return;
int mid=(l+r)>>1,pos;double mx=0;
for(int i=L;i<=mid&&i<=R;i++) if(get(i,mid)>mx) mx=get(i,mid),pos=i;
ans[id[mid]]=max(ans[id[mid]],mx);
solve(l,mid-1,L,pos);solve(mid+1,r,pos,R);
}
void rev(int n){for(int i=1,j=n;i<j;i++,j--) swap(id[i],id[j]),swap(a[i],a[j]);}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),id[i]=i,f[i]=sqrt(i);
solve(1,n,1,n);rev(n);solve(1,n,1,n);
for(int i=1;i<=n;i++) printf("%d\n",(int)ceil(ans[i]-a[n-i+1]));
}