其实看到这道题是毫无思路的。
先简化思路,对于每个i,求max{aj+sqrt(|i-j|)}-ai
把这个式子分成前后两部分,即max(max{aj+sqrt(i-j)},max{ak+sqrt(k-i)})-ai (j<=i<=k)
然后,我们发现其实这个式子是有单调性的,所以可以用那种二分优化单调性dp的方式做。
整体二分是这种单调性dp的一种写法。
void solve1(int l,int r,int L,int R) { if (l>r) return; int mid=(l+r)/2; int pos=0; double mx=0.0; for (int i=L;i<=R && i<=mid;i++) if ((double)a[i]+sqrt(mid-i)>=mx) pos=i,mx=(double)a[i]+sqrt(mid-i); f[mid]=a[pos]+ceil(sqrt(mid-pos)); solve1(l,mid-1,L,pos); solve1(mid+1,r,pos,R); }
l,r是当前要计算的序列,L,R是决策的两端点,每次暴力计算mid位置的值,然后把整个序列分为两部分,也算是一种整体二分的写法吧。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<iostream> #define maxn 500010 using namespace std; int a[maxn]; int f[maxn],g[maxn]; int n,m; void solve1(int l,int r,int L,int R) { if (l>r) return; int mid=(l+r)/2; int pos=0; double mx=0.0; for (int i=L;i<=R && i<=mid;i++) if ((double)a[i]+sqrt(mid-i)>=mx) pos=i,mx=(double)a[i]+sqrt(mid-i); f[mid]=a[pos]+ceil(sqrt(mid-pos)); solve1(l,mid-1,L,pos); solve1(mid+1,r,pos,R); } void solve2(int l,int r,int L,int R) { if (l>r) return; int mid=(l+r)/2; int pos=0;double mx=0.0; for (int i=R;i>=L && i>=mid;i--) if ((double)a[i]+sqrt(i-mid)>=mx) pos=i,mx=(double)a[i]+sqrt(i-mid); g[mid]=a[pos]+ceil(sqrt(pos-mid)); solve2(l,mid-1,L,pos); solve2(mid+1,r,pos,R); } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); solve1(1,n,1,n); solve2(1,n,1,n); for (int i=1;i<=n;i++) printf("%d\n",max(f[i],g[i])-a[i]); return 0; }