BZOJ2216 [POI2011] Lightning Conductor
https://www.luogu.com.cn/problem/P3515
即计算最大的aj-ai+sqrt(|i-j|)。先考虑i>j的情况,此时可以验证有决策单调性:当决策点x,y和待计算点i,j满足x<y<i<j时,证明:若ax-ai+sqrt(|i-x|)<ay-ai+sqrt(|i-y|),则必有ax-aj+sqrt(|j-x|)<ay-aj+sqrt(|j-y|)。证明方法把前式的i移到一边然后分子有理化发现单调性即可。i<j时把a反过来同理。
决策单调性的一般方法是:队列维护决策点及其控制区间(这个控制区间是i~n的,是在仅考虑1~i-1的决策点时的情况,而不一定是最终的),然后用队头(此时它控制i)更新dp(i),并加入决策点i。加入时,从队尾弹出“用i更新某区间左端点,比用原控制该区间的点来更新该区间左端点更好”的区间(即把他们归为i的控制区间),然后在弹完后的队尾的那个区间找到“恰好归i控制的下标最小的点”,从而把该区间劈成两半。劈完后的后一半与原来弹出的那些区间之并即成为i的控制区间。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 5 int n; 6 #define maxn 500011 7 int head,tail,a[maxn]; 8 struct quenode{int p,l,r;}que[maxn]; 9 double f[maxn],g[maxn]; 10 double cal(int j,int i) {return a[j]+sqrt(i-j)-a[i];} 11 void dp(double *f) 12 { 13 head=tail=1; 14 for (int i=1;i<=n;i++) 15 { 16 if (head<tail) f[i]=cal(que[head].p,i); 17 que[head].l++; 18 if (head<tail && que[head].l>que[head].r) head++; 19 while (head<tail && cal(que[tail-1].p,que[tail-1].l)<cal(i,que[tail-1].l)) tail--; 20 if (head==tail) que[tail++]=(quenode){i,i+1,n}; 21 else 22 { 23 int L=que[tail-1].l,R=que[tail-1].r+1; 24 while (L<R) 25 { 26 int mid=(L+R)>>1; 27 if (cal(que[tail-1].p,mid)>cal(i,mid)) L=mid+1; 28 else R=mid; 29 } 30 if (L<=n) 31 { 32 que[tail-1].r=L-1; 33 que[tail++]=(quenode){i,L,n}; 34 } 35 } 36 } 37 } 38 39 int main() 40 { 41 scanf("%d",&n); 42 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 43 dp(f); 44 for (int i=1;i+i<=n;i++) {int t=a[i]; a[i]=a[n-i+1]; a[n-i+1]=t;} 45 dp(g); 46 for (int i=1;i<=n;i++) printf("%.0lf\n",max(0.,ceil(max(f[i],g[n-i+1])))); 47 return 0; 48 }