zoukankan      html  css  js  c++  java
  • [BOI2004]Sequence

    题面描述

    给定整数数组$a_1,a_2,a_3...a_n$,求递增数组$b_1,b_2,b_3...b_n$

    使得$|a_1 - b_1| + |a_2 - b_2| + ... + |a_n - b_n|$最大

    吐槽:

    这道题不是人能想出来的,太神了

    很有收获?$or; not;?$

    题解:

    考虑$b$数组严格递增这个条件过于苛刻,期望$b$数组不严格递增

    那么,由于$b_1 < b_2 < ... < b_n$,有$b_1 - 1 leq b_2 - 2 ... leq b_n - n$

    我们不妨考虑新的$b'_i = b_i - i$

    在$b'_i$对$a'_i = a_i - i$取到最优时,$b_i$同时对$a_i$取到最优

    这时,$b‘_i$就可以相等了

    两个性质

    1. 对于$a'_1 leq a'_2 leq ... leq a'_n$,我们取$b'i = a'i$最优

    2. 对于$a'_1 geq a'_2 geq ... geq a'_n$,我们取$b'i$为中位数最优

    并且由于$b'_i$相等,$a'_1, a'_2...a'_n$可以任意的排列

    因此,我们不妨假设已经处理好了前$i - 1$个数,正要加入第$i$个数

    我们假设维护出来的$b'_i$相同的并成一个区间,$b'_i$取这个区间的中位数

    对于$a'_i > b'_{i - 1}$,取$b'_i = a'_i$时,相等于递增序列

    否则$a'_i < b'_{i - 1}$,我们把$i$并入$b'_{i - 1}$,得到新的中位数(因为顺序任意,所以怎么合都没问题),并继续检查

    那么,我们要做的事其实就是

    1. 快速合并两个区间中的数

    2. 确定出区间中的中位数

    左偏树就是不错的选择

    复杂度$O(nlog n)$

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #define sid 1000500
    #define ll long long
    #define ri register int
    #define ls(o) t[o].ls
    #define rs(o) t[o].rs
    using namespace std;
    
    char RR[23456];
    #define isdigit(u) (u >= '0' && u <= '9')
    extern inline char gc() {
        static char *S = RR + 23333, *T = RR + 23333;
        if(S == T) S = RR, fread(RR, 1, 23333, stdin);
        return *S ++;
    }
    inline int read() {
        int o = 0, w = 1; char c = gc();
        while(!isdigit(c)) {if(c == '-') w = -1; c = gc();}
        while(isdigit(c)) o = o * 10 + c - '0', c = gc();
        return o * w;
    }
    
    int n, cnt;
    int rt[sid], ld[sid], rd[sid], a[sid];
    struct reHeap {
        int ls, rs, sz, npl, key;
    } t[sid];
    
    inline void update(int o) {
        t[o].sz = t[ls(o)].sz + t[rs(o)].sz + 1;
        t[o].npl = t[rs(o)].npl + 1;
    }
    
    int merge(int x, int y) {
        if(!x || !y) return x + y;
        if(t[x].key < t[y].key) swap(x, y);
        rs(x) = merge(rs(x), y);
        if(t[rs(x)].npl > t[ls(x)].npl) swap(ls(x), rs(x));
        update(x); return x;
    }
    
    int main() {
        n = read();
        for(ri i = 1; i <= n; i ++) {
            ld[++ cnt] = rd[cnt] = rt[cnt] = i;
            a[i] = t[i].key = read() - i; t[i].sz = 1;
            while(cnt > 1 && t[rt[cnt]].key < t[rt[cnt - 1]].key) {
                cnt --; rd[cnt] = rd[cnt + 1];
                rt[cnt] = merge(rt[cnt], rt[cnt + 1]);
                while(t[rt[cnt]].sz * 2 > rd[cnt] - ld[cnt] + 2)
                rt[cnt] = merge(ls(rt[cnt]), rs(rt[cnt]));
            }
        }
        ll ans = 0;
        for(ri i = 1; i <= cnt; i ++)
        for(ri j = ld[i]; j <= rd[i]; j ++)
        ans += abs(t[rt[i]].key - a[j]);
        printf("%lld
    ", ans);
        for(ri i = 1; i <= cnt; i ++)
        for(ri j = ld[i]; j <= rd[i]; j ++)
        printf("%d ", t[rt[i]].key + j);
        return 0;
    }
  • 相关阅读:
    基于NFS的PV动态供给(StorageClass)
    Helm学习笔记
    k8s日志收集方案
    okhttputils【 Android 一个改善的okHttp封装库】使用(三)
    PopupWindowMenuUtil【popupwindow样式菜单项列表】
    NewBuiltBottomSheetDialog【新建底部对话框】
    NormalDialogFragmentDemo【普通页面的DialogFragment】
    ArticleRemoveDelDialog【基于AlertDialog的回收删除对话框】
    ConfirmCancelBottomSheetDialog【确认取消底部对话框】
    Android-PickerView【仿iOS的PickerView控件,并封装了时间选择和选项选择这两种选择器】使用
  • 原文地址:https://www.cnblogs.com/reverymoon/p/9358978.html
Copyright © 2011-2022 走看看