zoukankan      html  css  js  c++  java
  • 奇技淫巧训练之五

    https://www.luogu.org/problem/P2501

    这道题真的很好,写题解的人写的也真好

    第一问

    如果要保留 a[i],a[i] 和 a[j],a[j] ,
    前提是:他们中间的数本身就合法,或者他们中间的数可以被改成合法。

    比如,17,50,50,50,19这个序列,看上去17和19能保留,但如果保留,中间三个50怎么改都不会单调上升。

    可见只有 a[j],a[j] 和 a[i],a[i] 的差大于等于 j-i才允许同时保留两者,否则中间一定出错。

    a[j] - a[i] >= j - i移项:

    a[j] - j >= a[i] - i此为保留条件。

    所以把根据 a 预处理出新序列 b[i] = a[i] - i,

    然后找 b的最长不下降子序列的长度,就是最多能保留的个数。

    第二问

    把 a变成严格单调上升序列等同于:在 b 上对应地处理,并把 b 变成单调不降。

    现在就考虑 b怎么改才能代价最小。

    注意:(一) b 的最长不下降子序列可能有多个。

    (二) b 的最长不下降子序列中,任何两个相邻的元素 b[i], b[j]

    (相邻指的是在子序列中相邻,而在 b 中不一定相邻) 之间

    ,绝对不存在另一个大小介于两者之间的元素。

    否则取这个元素,保证合法,而且可以使不降序列更长。

    所以每个被保留的 b[i]和 b[j] 之间的元素全部不合法。怎么改变这两者之间的元素?

    img

    黑线表示修改以后的海拔。显然所有方法都是阶梯(横线看作台阶)。此方法看上去很糟。

    img

    如果台阶上的“上升点数目”大于“下降点数目”,那么把台阶下降(以满足那么多“上升点”的要求!),

    直到它下降到和左边台阶一样高,也就和左边变成了同一块台阶。

    反之,就把台阶上升到和右边台阶一样高。然后继续缩减台阶。

    如果有Up = Down的,则台阶向上向下都可以(代价不变,台阶数目减少)。

    这个过程始终保证合法、保证代价减小或不变

    这样变化到End,一定只剩下两块台阶,左边的高 b[i],右边的高 b[j] 。

    以上说明,最优解一定是(或者说,一定可以是)

    左边 b[i] to b[k] 全部变成 b[i] 且

    右边 b[k+1] to b[j] 全部变成 b[j]

    的形态。

    如果最优解不是这样,我们可以无偿甚至减偿来变成这种形态

    每个区间的 k可以枚举。

    code by std

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    inline int getint()
    {
        int res = 0, neg = 0, ch = getchar();
        while(!(isdigit(ch) || ch == '-') && ch != EOF)ch = getchar();
        if(ch == '-')neg = 1;
        while(isdigit(ch))res = (res << 3) + (res << 1) + (ch - '0'), ch = getchar();
        return neg ? -res : res;
    }
    #define re register
    #define LL long long
    #define INF 2147483647
    inline LL min(LL x, LL y) {return x < y ? x : y; }
    inline LL abs(LL x) {return x < 0 ? -x : x; }
    int n;
    int a[40010], b[40010];
    int Minof[40010], len = 0, Longest[40010];
    int first[40010], to[40010], nxt[40010], cnt = 0;
    LL f[40010];
    LL sumL[40010], sumR[40010];
    inline void addE(int u, int v)
    {
        ++cnt;
        to[cnt] = v;
        nxt[cnt] = first[u];
        first[u] = cnt;
    }
    int main()
    {
        n = getint();
        for(re int i = 1; i <= n; ++i)
            a[i] = getint(), b[i] = a[i]-i;
        b[n+1] = INF;
        //二分法找最长不降子序列 
        for(re int i = 1; i <= n+1; ++i)
        {
            int l = 0, r = len;
            while(l < r)
            {
                int mid = (l + r + 1) >> 1;
                if(Minof[mid] <= b[i])
                    l = mid;
                else
                    r = mid - 1;
            }
            if(l == len) 
                ++len;
            Longest[i] = l+1;
            addE(Longest[i], i);
            Minof[l+1] = b[i];
        }
        addE(0, 0);
        printf("%d
    ", n-(len-1));
        memset(f, 20, sizeof(f));
        b[0] = -INF;f[0] = 0;
        for(re int i = 1; i <= n+1; ++i) 
        {
            for(re int p = first[Longest[i]-1]; p; p = nxt[p])
            {
                int u = to[p];
                if(u > i || b[u] > b[i])
                    continue;
                sumL[u] = 0; 
                for(re int k = u+1; k <= i-1; ++k)
                    sumL[k] = sumL[k-1] + abs(b[k] - b[u]);   
                sumR[i-1] = 0;
                for(re int k = i-2; k >= u; --k)
                    sumR[k] = sumR[k+1] + abs(b[k+1] - b[i]);
                for(re int k = u; k <= i-1; ++k)
                    f[i] = min(f[i], f[u] + sumL[k] + sumR[k]);
            }   
        }
        printf("%lld
    ", f[n+1]);
        return 0;
    }
    
  • 相关阅读:
    《Spring_Four》第二次作业 基于Jsoup的大学生考试信息展示系统开题报告
    《Spring_Four》第一次作业:团队亮相
    4.11jsp
    4.7jsp
    3.17jsp
    3.24jsp
    3.10jsp
    3.4软件测试
    回文串
    博客园第二次作业
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/11627570.html
Copyright © 2011-2022 走看看