zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",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
    ",ceil(max(f1[i],f2[i])));
      return 0;
    }
  • 相关阅读:
    asp.net 实现pdf、swf等文档的浏览
    VS NuGet加载本地程序包
    《大型网站技术架构》读书笔记
    全排列组合算法
    GDI+绘制半圆按钮
    oracle dblink 查询 tns:无法解析指定的连接标识符
    最少有多少鸡蛋(求最小公倍数)
    杨辉三角
    Android开发面试题(一)
    2015年11月系统架构设计师案例分析题
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6396378.html
Copyright © 2011-2022 走看看