BZOJ2216: [Poi2011]Lightning Conductor
n个数,对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt(abs(i-j))。
这就是要找max(Aj-Ai+sqrt(abs(i-j))向上取整。把他分成两部分,Max(Max (Aj+sqrt(i-j)) , Max (Ak+sqrt(k-i))) j<=i<=k。
这个东西是有决策单调性的,假设一个Ax是从Aj那里转移过来的,先考虑j<x,那么j是比任意一个k,k<x,都要好,也就是
$A_j-A_x+sqrt(x-j) \geq A_k-A_x+sqrt(x-k)$
整理下就是
$A_j-A_k \geq sqrt(x-k)-sqrt(x-j) $
右边那个东西会随x的增大而减小,至于为什么,详见高中数学选修2-2函数求导。
反过来同理。这样的话,每次决策完一个点可以把序列分成两个部分分别再决策,这样可以抽象成一个若干层的树,每一层的复杂度都是n。为了使复杂度最小,采用整体二分。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 //#include<queue> 6 #include<cmath> 7 //#include<iostream> 8 using namespace std; 9 10 int n; 11 #define maxn 500011 12 int a[maxn],f[maxn],g[maxn]; 13 void solve1(int l,int r,int L,int R) 14 { 15 if (l>r) return; 16 int mid=(l+r)>>1,pos=0; 17 double Max=0; 18 for (int i=L;i<=R && i<=mid;i++) 19 if (1.0*a[i]+sqrt(mid-i)>Max) pos=i,Max=1.0*a[i]+sqrt(mid-i); 20 f[mid]=ceil(Max)-a[mid]; 21 solve1(l,mid-1,L,pos); 22 solve1(mid+1,r,pos,R); 23 } 24 void solve2(int l,int r,int L,int R) 25 { 26 if (l>r) return; 27 int mid=(l+r)>>1,pos=0; 28 double Max=0; 29 for (int i=R;i>=L && i>=mid;i--) 30 if (1.0*a[i]+sqrt(i-mid)>Max) pos=i,Max=1.0*a[i]+sqrt(i-mid); 31 g[mid]=ceil(Max)-a[mid]; 32 solve2(l,mid-1,L,pos); 33 solve2(mid+1,r,pos,R); 34 } 35 int main() 36 { 37 scanf("%d",&n); 38 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 39 solve1(1,n,1,n); 40 solve2(1,n,1,n); 41 for (int i=1;i<=n;i++) printf("%d\n",max(f[i],g[i])); 42 return 0; 43 }