BZOJ4850/BZOJ2216 JSOI2016灯塔/Poi2011Lightning Conductor(决策单调性)

  即对每个i最大化hj-hi+sqrt(|i-j|)。先把绝对值去掉,正反各做一次即可。注意到当x>y时,sqrt(x+1)-sqrt(x)<sqrt(y+1)-sqrt(y),所以若对于i选择j比选择k更优(j>k),对于i+1~n也会是这样,即满足决策单调性(虽然并不能算作dp)。

  可以这样使用决策单调性优化:维护一个栈,存储当前考虑的这些位置中每个位置向哪个区间转移最优。转移时在栈中二分,然后考虑更新栈,如果新加入的位置向栈顶的整个区间转移都是最优的,直接将栈顶位置弹出,否则二分找一个区间的分割点,最后把这个新位置加入栈中即可。

  寻找决策区间时小心不要把已更新过的位置算进去。注意维护决策时不能对答案取整,否则会影响决策区间。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,a[N],q[N],l[N],r[N],id[N],top;
double ans[N];
double calc(int i,int j){return a[j]-a[i]+sqrt(i-j);}
void work()
{
    l[1]=1,r[1]=n,id[1]=1,top=1;
    for (int i=2;i<=n;i++)
    {
        int left=1,right=top,x;
        while (left<=right)
        {
            int mid=left+right>>1;
            if (l[mid]<=i&&r[mid]>=i) {x=mid;break;}
            else if (r[mid]<i) left=mid+1;
            else right=mid-1;
        }
        ans[i]=max(ans[i],calc(i,id[x]));
        while (top&&l[top]>=i&&calc(l[top],id[top])<calc(l[top],i)) top--;
        left=max(l[top],i),right=r[top],x=r[top]+1;
        while (left<=right)
        {
            int mid=left+right>>1;
            if (calc(mid,id[top])<calc(mid,i)) x=mid,right=mid-1;
            else left=mid+1;
        }
        r[top]=x-1;
        if (x<=n) top++,l[top]=x,r[top]=n,id[top]=i;
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4850.in","r",stdin);
    freopen("bzoj4850.out","w",stdout);
    const char LL[]="%I64d
";
#else
    const char LL[]="%lld
";
#endif
    n=read();
    for (int i=1;i<=n;i++) a[i]=read();
    work();reverse(a+1,a+n+1),reverse(ans+1,ans+n+1);
    work();for (int i=n;i;i--) printf("%.0f
",ceil(ans[i]));
    return 0;
}
原文地址:https://www.cnblogs.com/Gloid/p/10003442.html