bzoj2216[POI2011] Lightning Conductor

题意:
已知一个长度为n的序列a1,a2,...,an。
对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt(abs(i-j))

n<=500000

决策单调性的新写法get

首先暴力的做法是对于每个i枚举每个j,方便起见分别考虑j在i左侧和右侧的情况以去掉绝对值。不妨只考虑j在i左侧,右侧是对称的.

那么我们只需要在每个ai的左侧找到一个aj使得aj+sqrt(i-j)最大,由这个aj确定此处p的值(右侧也要对称地找一遍)

然后我们发现这是一个1D1D动态规划,这题范围这么大就猜测是斜率优化或者决策单调性,打表发现有决策单调性.

决策单调性有两种nlogn的写法,一种是单调栈+二分(《1D1D动态规划优化初步》里头的方法),一种是分治,假如我们知道f[l],f[l+1]….f[r-1],f[r]这些状态的决策点在区间[L,R]内,我们就可以取[l,r]区间的中点mid,暴力出f[mid]的决策g,然后就知道f[l]…f[mid-1]这些状态的决策点在区间[L,g]内,f[mid+1]…f[r]这些状态的决策点在区间[g,R]内,就可以分治了。同一深度的所有暴力循环找决策的复杂度加起来是O(n),又因为分治的时候mid的选取是均分的所以深度是logn,于是这个分治做法也是O(nlogn)的,注意这道题分治的时候有个边界的处理,具体看代码.

虽然我也想知道为什么,但我们联赛之前考过这道题…嗯,我当时还考场AC了…囧…

当时写的弱鸡证明(可能有错,欢迎捉虫):

不妨讨论j在i左侧的情况。

假设j1<j2<i1<i2,我们只需证明:如果j2在i1处比j1优,那么j2在i2处也比j1优。

也就是说:我们需要从aj1+sqrt(i1-j1)>aj2+sqrt(i1-j2) 推出aj1+sqrt(i2-j1)>aj2+sqrt(i2-j2)

不妨将等式的左右两边都看作关于i的函数,那么上面两个式子分别比较了两个函数在i1和i2处的大小。我们需要考虑这两个函数在自变量i1变为i2时函数值的变化。

当我们把左侧aj1+sqrt(i1-j1)换为aj1+sqrt(i2-j1),这个式子增加了sqrt(i2-j1)-sqrt(i1-j1)

当我们把右侧aj2+sqrt(i1-j2)换为aj2+sqrt(i2-j2),这个式子增加了sqrt(i2-j2)-sqrt(i1-j2)

考虑y=sqrt(x)这个函数,它有一个性质:x越大,y随x的增长越慢,也就是说图像趋于平缓.因此,如果j1>j2,我们可以直观地认为sqrt(i2-j1)-sqrt(i1-j1)> sqrt(i2-j2)-sqrt(i1-j2),因为i2-j1和i1-j1的差值为i2-i1。i2-j2和i1-j2的差值也为i2-i1,而i2-j1比i2-j2小。

更加严谨的证明:可以把sqrt(i2-j1)-sqrt(i1-j1)用平方差公式化为(i2-i1)/(sqrt(i2-j1)+sqrt(i1-j1)),然后显然可以比较两边增加量的大小。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=500005;
int a[maxn];
int s[maxn],l[maxn],r[maxn],top,pt;
double f1[maxn],f2[maxn];
int binary(int l,int r,int x,int y){
  while(l<=r){
    int mid=(l+r)>>1;
    if(y>=mid||sqrt(mid-y)+a[y]<sqrt(mid-x)+a[x])l=mid+1;
    else r=mid-1;
  }
  return l;
}
int main(){
  int n;scanf("%d",&n);
  for(int i=1;i<=n;++i)scanf("%d",&a[i]);
  s[++top]=1;l[top]=2;r[top]=n;int pt=1;
  for(int i=2;i<=n;++i){
    while(r[pt]<i)++pt;//printf("%d\n",s[pt]);
    f1[i]=sqrt(i-s[pt])+a[s[pt]]-a[i];
    while(top&&l[top]>i&&(sqrt(l[top]-i)+a[i])>(sqrt(l[top]-s[top])+a[s[top]]))top--;
    int tmp=binary(l[top],r[top],s[top],i);
    if(tmp==r[top]+1)continue;
    r[top]=tmp-1;s[++top]=i;l[top]=tmp;r[top]=n;
  }
  for(int i=1;i*2<=n;++i){
    swap(a[i],a[n-i+1]);
  }
  top=0;
  s[++top]=1;l[top]=2;r[top]=n;pt=1;
  for(int i=2;i<=n;++i){
    while(r[pt]<i)++pt;//printf("%d\n",s[pt]);
    f2[i]=sqrt(i-s[pt])+a[s[pt]]-a[i];
    while(top&&l[top]>i&&(sqrt(l[top]-i)+a[i])>(sqrt(l[top]-s[top])+a[s[top]]))top--;
    int tmp=binary(l[top],r[top],s[top],i);
    r[top]=tmp-1;s[++top]=i;l[top]=tmp;r[top]=n;
  }
  for(int i=1;i*2<=n;++i){
    swap(f2[i],f2[n-i+1]);
  }
  for(int i=1;i<=n;++i)printf("%.0f\n",max(0.0,ceil(max(f1[i],f2[i]))));
  return 0;
}
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=500005;
int a[maxn];
double f1[maxn],f2[maxn];
void solve1(int l,int r,int L,int R){
  if(l>r)return;
  int mid=(l+r)>>1;
  int g=0;double tmp;
  f1[mid]=a[mid];//有可能j!=mid时得不到大于0的p,所以先给f初始一个值
  for(int i=L;i<=R&&i<mid;++i){
    if((tmp=a[i]+sqrt(mid-i))>f1[mid])f1[mid]=tmp,g=i;
  }
  if(g==0)g=mid;
  f1[mid]-=a[mid];
  solve1(l,mid-1,L,g);solve1(mid+1,r,g,R);
}
void solve2(int l,int r,int L,int R){
  if(l>r)return;
  int mid=(l+r)>>1;
  int g=0;double tmp;
  f2[mid]=a[mid];
  for(int i=R;i>=L&&i>mid;--i){
    if((tmp=a[i]+sqrt(i-mid))>f2[mid])f2[mid]=tmp,g=i;
  }
  if(g==0)g=mid;
  f2[mid]-=a[mid];
  solve2(l,mid-1,L,g);solve2(mid+1,r,g,R);
}
int main(){
  int n;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("%.0f\n",ceil(max(f1[i],f2[i])));
  return 0;
}

 

posted @ 2017-02-14 10:08  liu_runda  阅读(233)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难