• [BalticOI 2004] Sequence


    看黄源河左偏树的论文时找过去的,结果发现了个超级牛的解法 /se ,然后莫名其妙就变成了洛谷和 darkbzoj 的最优解了 /fad

    先把 $a_i$ 全部减去一个 $i$ ,然后 $b_i$ 的限制就变成了不降序列了,最后输出加回来即可

    然后可以列出一个非常 $ ext{naive}$ 的 $ ext{DP}$ :

    设 $f_{i,j}$ 为转移到第 $i$ 个,当前的 $b_i$ 等于 $j$ 的最小代价

    那么转移也很 $ ext{naive}$ ,在此直接列出:

    $f_{i,j}=|a_i-j| + $minlimits_{k<=j} f_{i-1,k}$

    将其改写为 $f_i(x)$ ,很明显 $f_i(x)$ 是一个凸函数(证明可用数学归纳法),于是 $f_i(x)$ 的取值如下

    设 $L$ 为 $f_{i-1}(x)$ 斜率为 $0$ 的段的左端点

    $f_i(x)=|a_i-x| + egin{cases} f_{i-1}(x) & x<L \ f_{i-1}(L) & x ge L end{cases}$

    于是我们只需要支持每次加上一个绝对值函数即可,直接分类讨论:

    1、 $a_i ge L$ ,那么显然此时 $L$ 向右移动变为 $a_i$ ,其他不变

    2、 $a_i < L$ ,那么 $a_i$ 以左斜率 $-1$  ,以右斜率 $+1$ ,于是显然 $L$ 此时不再是斜率为 $0$ 的左端点了,左端点改变为原本斜率为 $-1$ 的左端点或者是 $a_i$ (看哪个大)

    于是发现又是比较套路的堆优化凸函数(自己取的名字),直接用优先队列即可,答案就是每次转移左端点的代价,注意向右转移是没有代价的,所以只有第二种有代价

    但是如何输出方案呢?这里给出一种方法,每次先把堆顶(是转移以后的 $L$ 端点)记为答案,然后从 $b_{n-1}$ 开始,往前推,让 $b_i=min(b_i,b_{i+1})$ ,为什么这是对的?

    看最初的 $ ext{naive}$ 想法,注意到这是一个 $ ext{DP}$ ,所以如果对于当前端点是最优的,那么肯定前面是能够有方案转移成它的,又因为若 $b_i>b_{i+1}$ ,也就是 $L_i>b_{i+1}$ 那么根据我们上面列出的 $f_i(x)$ 的取值,当前情况最优的就是 $f_{i+1}(x)$ 由 $f_i(x)$ 转移过来(第一条)

    这里就贴上输出方案的代码

    $code$ :

    #include<cstdio>
    #include<cctype>
    #include<queue>
    
    using namespace std;
    
    #define maxn 1001001
    
    template<class T>
    
    inline T read(){
        T r=0,f=0;
        char c;
        while(!isdigit(c=getchar()))f|=(c=='-');
        while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
        return f?-r:r;
    }
    
    inline int min(int a,int b){
        return a<b?a:b;
    }
    
    int n,s[maxn];
    
    long long ans;
    
    priority_queue<int> q;
    
    int main(){
        n=read<int>();
        q.push(s[1]=read<int>()-1);
        for(int i=2;i<=n;i++){
            int v=read<int>()-i;
            int L=q.top();
            if(L>v){
                ans+=L-v;
                q.pop();
                q.push(v);
            }
            q.push(v);
            s[i]=q.top();
        }
        for(int i=n-1;i>=1;i--)s[i]=min(s[i],s[i+1]);
        printf("%lld
    ",ans);
        for(int i=1;i<=n;i++)printf("%d ",s[i]+i);
        return 0;
    }
  • 相关阅读:
    Handling Configuration Changes with Fragments
    Android基础——Fragment与Activity交互
    Android基础——Fragment与Activity交互
    Activity和Fragment生命周期对比
    Activity和Fragment生命周期对比
    移动端与PHP服务端接口通信流程设计(基础版)
    移动端与PHP服务端接口通信流程设计(基础版)
    ROW/COW 快照技术原理解析
    ROW/COW 快照技术原理解析
    OpenStack 实现技术分解 (5) 应用开发 — 使用 OpenStackClients 进行二次开发
  • 原文地址:https://www.cnblogs.com/wyzwyz/p/14054732.html
Copyright © 2020-2023  润新知