zoukankan      html  css  js  c++  java
  • [题解/模板]luogu_P3515_LightningConductor(决策单调性

    已知一个长度为n的序列a1,a2,...,an。

    对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt(abs(i-j))

    每个点的$p=max(a[j]-a[i]+sqrt{|i-j|}$,可以想到dp,$f[i]=max(a[j]-a[i]+sqrt{|i-j|}$

    绝对值比较常见的做法可以分类讨论去掉,比如我们只处理$j<i$的情况,然后在倒着做一遍就可以了

    对于这个根号,整理一下式子啥的好像并不能用斜率优化之类的,不妨考虑决策单调性,

    证明:

    需证:对于两个决策点$p1,p2$和$i1<i2$,若从$p2$转移到$i1$比从$p1$转移到$i1$优,则从$p2$转移到$i2$也比$p1$优

    所以已知$a[p1]-a[i1]+sqrt{p1-i1}<a[p2]-a[i1]+sqrt{p2-i1}$,求证$a[p1]-a[i2]+sqrt{p1-i2}<a[p2]-a[i2]+sqrt{p2-i2}$

    发现两个式子化简后只有根号里有$i1,i2$,所以可以从根号下手,注意$i1<i2$,也就是说$p-i1>p-i2$,相当于根号这个函数的自变量变小了一点,并且两边变小的值相同

    那么就可以想到关于斜率/导数之类的东西,也就是根号函数斜率递减,在前面和后面自变量变化相同的值,函数值变化有一个大小关系:对于$i<j,sqrt{i-Delta}-sqrt{i}>sqrt{j-Delta}-sqrt{j}$,变化量相同时自变量小的函数值变化量大,那么单调性就显然了

    并没写分治做法

    二分栈流程:

    1.排除队头所有右端点小于$i$的元素,然后把队头元素左端点设为$i$

    2.用队头更新$f[i]$

    3.排除所有队尾比$i$不优秀的决策

    4.若队列空则直接插入$i$,否则:

      二分找到转折点$pos$,若$pos$不与队尾左端点重合,修改队尾元素右端点为$pos-1$,否则弹出队尾

      若$pos<=n$插入$i$

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int maxn=500009;
    int n,a[maxn];
    double f[maxn],g[maxn],sq[maxn];
    struct node{
        int p,l,r;
    }q[maxn];
    double calc(int j,int i){
        return a[j]+sq[i-j];
    }
    int bound(int t,int i){
        int pos=q[t].r+1,l=q[t].l,r=q[t].r;
        while(l<=r){
            int mid=l+r>>1;
            if(calc(q[t].p,mid)<calc(i,mid))pos=mid,r=mid-1;
            else l=mid+1;
        }
        return pos;
    }
    void work(){
        int head=1,tail=0,pos;
        for(int i=1;i<=n;i++){
            while(head<=tail && q[head].r<i)head++;q[head].l=i;
            f[i]=max(f[i],calc(q[head].p,i)-a[i]);
            while(head<=tail && calc(q[tail].p,q[tail].l)<calc(i,q[tail].l))tail--;
            if(head>tail)q[++tail]=(node){i,i,n};
            else{
                int pos=bound(tail,i);
                if(pos!=q[tail].l)q[tail].r=pos-1;
                else tail--;
                if(pos<=n)q[++tail]=(node){i,pos,n};
            }
        }
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),sq[i]=sqrt(i);
        work();
        for(int i=1,j=n;i<j;i++,j--)
        swap(a[i],a[j]),swap(f[i],f[j]);
        work();
        for(int i=n;i>=1;i--)
        printf("%d
    ",(int)(ceil(f[i])));
        
    }
  • 相关阅读:
    [译]reset, checkout和revert
    [译]merge vs rebase
    [译]使用branch
    [译]git push
    [译]git pull
    [译]git fetch
    [译]git remote
    Java RTTI机制与反射机制
    Java反射的一些理解
    Java中的异常处理:何时抛出异常,何时捕获异常?
  • 原文地址:https://www.cnblogs.com/superminivan/p/11489572.html
Copyright © 2011-2022 走看看