[Baltic2004]Sequence 题解

[Baltic2004]Sequence 题解

题目地址:洛谷:【P4331】[BOI2004]Sequence 数字序列 – 洛谷、BZOJ:Problem 1367. — [Baltic2004]sequence

题目描述

1367 1 - [Baltic2004]Sequence 题解

输入输出格式

输入格式:
1367 2 - [Baltic2004]Sequence 题解

输出格式:
第一行输出最小的绝对值之和。
第二行输出序列 bi ,若有多种方案,只需输出其中一种。

输入输出样例

输入样例#1:

5
2 5 46 12 1

输出样例#1:

47
2 5 11 12 13

说明

【数据范围】
40%的数据 n≤5000
60%的数据 n≤300000
100%的数据 n≤1000000 , 0≤a_i≤2×10^9

题解

考虑当所求序列$z_i$是不降的情况应该如何解决。我们有两种情况:

  1. $t_1 \leq t_2 \leq \cdots \leq t_n$:$z_i=t_i$即可
  2. $t_1 \geq t_2 \geq \cdots \geq t_n$:取这些值的中位数作为所有的$z_i$值,这是由于取中位数的时候可以证明答案最小

因此,我们可以将$t$数列分为若干区间,每个区间的答案即其中位数,从而解决这个问题。
然而题目让我们求一个严格递增的序列,我们考虑将$t_i$处理成$t_i – i$,就可以用跟上面一致的方法解决这个问题,因为处理完毕后的数列不降等价于原数列单增。
实现上,我们对每个元素建一个可并堆,然后向左合并左边元素构成的可并堆(即左边的区间),直到这个区间的中位数不小于左边相邻区间的中位数。中位数可以利用大根堆维护,让堆的大小不大于$\left\lceil \frac{|S|}{2} \right\rceil$再取堆顶即可。
复杂度$O(n \log n)$。

代码

// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 1000005;

int n, a[MAXN];

struct Node {
    int ch[2], fa, val, dis, siz;
} tr[MAXN];
int rt[MAXN], l[MAXN], r[MAXN], htot;

inline int merge(int x, int y) {
    if(!x || !y) return x + y;
    if(tr[x].val < tr[y].val) std::swap(x, y);
    tr[x].ch[1] = merge(tr[x].ch[1], y);
    tr[x].siz = tr[tr[x].ch[0]].siz + tr[tr[x].ch[1]].siz + 1;
    tr[tr[x].ch[1]].fa = x;
    if(tr[tr[x].ch[0]].dis < tr[tr[x].ch[1]].dis) std::swap(tr[x].ch[0], tr[x].ch[1]);
    tr[x].dis = tr[tr[x].ch[1]].dis + 1;
    return x;
}

int main() {
    n = readint();
    for(int i = 1; i <= n; i++) {
        a[i] = readint() - i;
    }
    for(int i = 1; i <= n; i++) {
        htot++; rt[htot] = i;
        l[htot] = r[htot] = i;
        tr[i].val = a[i]; tr[i].siz = 1;
        while(htot > 1 && tr[rt[htot]].val < tr[rt[htot - 1]].val) {
            htot--;
            rt[htot] = merge(rt[htot], rt[htot + 1]);
            r[htot] = r[htot + 1];
            while(tr[rt[htot]].siz * 2 > r[htot] - l[htot] + 2) {
                rt[htot] = merge(tr[rt[htot]].ch[0], tr[rt[htot]].ch[1]);
            }
        }
    }
    LL ans = 0;
    for(int i = 1; i <= htot; i++) {
        int w = tr[rt[i]].val;
        for(int j = l[i]; j <= r[i]; j++) {
            ans += abs(w - a[j]);
        }
    }
    printf("%lld\n", ans);
    for(int i = 1; i <= htot; i++) {
        int w = tr[rt[i]].val;
        for(int j = l[i]; j <= r[i]; j++) {
            printf("%d ", w + j);
        }
    }
    return 0;
}


发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据