zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 040 E

    传送门:https://atcoder.jp/contests/agc040/tasks/agc040_e

    sol:

    考虑只做$operator 1$ 或者 $operator 2$
    只做$operator 1$的情况下:
    做的次数$+1$的情况 当且仅当存在$Ai>A_{i+1}$
    只做$operator 2$的情况下
    做的次数+1的情况 当且仅当$Ai<A_{i+1}$
    发现$operator 1$的操作并不会影响到 $operator 2$ 的操作
    所以我们可以考虑先做$operator 1$ 再统一做$operator 2$
    于是我们就发现原问题抽象一下
    就是 求两个序列$x$和$y$
    其中所有$x_i+y_i=A_i$
    贡献是$sum[xi>xi+1]$+$sum[yi<yi+1]$
    最小化这个贡献
    可以$dp$
    $dp_{i,j}$表示考虑了前$i$个数,当前这个$x_i$填入$j$的情况
    常规转移就是枚举上一位数字
    然后考虑加速这个转移
    观察转移过程
    发现对于$j$递增的情况,$dp$的值一定是单调不增的
    我们又可以发现 $dp_{i,0}<=dp_{i,A_i}+2$

    这个位置填入$0$的话,最多和填入$A_i$差异$2$ 我们可以修改$A_i$使得这个一定被达到
    于是我们就发现
    也就是说我们只需要知道的东西是 $dp_{i,0}$ 还有 其中有多少个$pos$是增的[这种$pos$绝对不会超过两个

    直接转移即可,转移$O(1)$,总效率$O(n)$

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    int N,a[200005];
    int main(){
        scanf("%d",&N);
        for (int i=1;i<=N;i++)
            scanf("%d",&a[i]);
        int ans=0,now=0,v[2]{};
        for (int i=1;i<=N+1;i++){
            int p[2]{0,a[i]-now};
            if (p[0]<p[1]) swap(p[0],p[1]);
            int nex[3]{1000000000,1000000000,0};
            for (int j=0;j<2;j++)
                for (int k=0;k<2;k++)
                    nex[j+k]=min(max(0,v[j]+p[k]),nex[j+k]);
            int d=nex[0]>a[i];
            for (int j=0;j<2;j++)
                v[j]=nex[j+d];
            ans+=d;
            now=a[i];
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    递归算法转换为非递归算法的技巧
    22. 平面列表
    14. 二分查找
    那点人生小智慧
    9. Fizz Buzz 问题
    8. 旋转字符串
    6. 合并排序数组:
    归并排序
    远方的她
    微服务体系下如何快速构建一个服务
  • 原文地址:https://www.cnblogs.com/si--nian/p/11791273.html
Copyright © 2011-2022 走看看