• 洛谷 P4331 [BalticOI 2004]Sequence 数字序列


    给定一个整数序列(a_1,a_2...a_n(nleq10^6)),求出一个递增序列(b_1<b_2<···<b_n),使得序列(a_i)(b_i)的各项之差的绝对值之和(|a_1 - b_1| + |a_2 - b_2| + ··· + |a_n - b_n|)最小

    这题挺有意思的

    首先我们可以把(a_i)加上(i),那么我们求的(b_i)也加上(i),这样不但对结果没有影响,而且可以把b的单调递增限制转化为单调不降

    然后我们考虑对a的一个单调不降的区间,那么答案肯定是(b_i=a_i)

    而对于a的单调下降的区间,答案为(b_i=a_{mid})(a_mid)为a的这段区间的中位数

    很容易想到a肯定是由许多这样的两种区间构成的,那么对于两段a的单调下降区间,我们可以对其合并再求中位数

    于是做法就出来了,我们开栈记录当前的最值(w_{cnt}),然后向后扫a,如果(a_i<w_{cnt}),那么就合并这两个点的中位数

    中位数还是比较好求的,因为是中间大,所以用堆存个(size),如果比区间的一半还大就pop掉,这样最大值就是中位数了

    这题还需要合并,所以开可并堆就好了

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    const int N = 1e6;
    using namespace std;
    int n,lc[N + 5],rc[N + 5],rt[N + 5],size[N + 5],L[N + 5],R[N + 5],cnt,dist[N + 5];
    long long ans,b[N + 5],a[N + 5],v[N + 5];
    int merge(int x,int y)
    {
        if (!x || !y)
            return x + y;
        if (v[y] > v[x])
            swap(x,y);
        rc[x] = merge(rc[x],y);
        if (dist[lc[x]] < dist[rc[x]])
            swap(lc[x],rc[x]);
        dist[x] = dist[rc[x]] + 1;
        return x;
    }
    long long myabs(long long x)
    {
        return x > 0 ? x : -x;
    }
    int main()
    {
        scanf("%d",&n);
        for (int i = 1;i <= n;i++)
            scanf("%lld",&v[i]),v[i] -= i;
        for (int i = 1;i <= n;i++)
        {
            a[++cnt] = v[i];
            L[cnt] = R[cnt] = rt[cnt] = i;
            size[cnt] = 1;
            while (cnt > 1 && a[cnt] < a[cnt - 1])
            {
                rt[cnt - 1] = merge(rt[cnt],rt[cnt - 1]);
                size[cnt - 1] += size[cnt];
                R[cnt - 1] = R[cnt];
                cnt--; 
                while (size[cnt] > (R[cnt] - L[cnt] + 1 + 2) / 2)   //上取整
                {
                    size[cnt]--;
                    rt[cnt] = merge(lc[rt[cnt]],rc[rt[cnt]]);
                }
                a[cnt] = v[rt[cnt]];
            }
        }
        for (int i = 1;i <= cnt;i++)
            for (int j = L[i];j <= R[i];j++)
                b[j] = a[i];
        for (int i = 1;i <= n;i++)
            ans += myabs(b[i] - v[i]);
        cout<<ans<<endl;
        for (int i = 1;i <= n;i++)
            printf("%lld ",b[i] + i);
        return 0;
    }
  • 相关阅读:
    爱摘苹果的小明
    盗梦空间
    九九乘法表
    谁是最好的Coder
    画图
    黑色帽子
    a letter and a number
    运维开发面试题
    python 守护进程daemon
    kubernets 应用部署
  • 原文地址:https://www.cnblogs.com/sdlang/p/13129387.html
Copyright © 2020-2023  润新知