[BOI2004]Sequence

时间:2023-03-10 07:25:03
[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(n\log 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[];
#define isdigit(u) (u >= '0' && u <= '9')
extern inline char gc() {
static char *S = RR + , *T = RR + ;
if(S == T) S = RR, fread(RR, , , stdin);
return *S ++;
}
inline int read() {
int o = , w = ; char c = gc();
while(!isdigit(c)) {if(c == '-') w = -; c = gc();}
while(isdigit(c)) o = o * + c - '', 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 + ;
t[o].npl = t[rs(o)].npl + ;
} 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 = ; i <= n; i ++) {
ld[++ cnt] = rd[cnt] = rt[cnt] = i;
a[i] = t[i].key = read() - i; t[i].sz = ;
while(cnt > && t[rt[cnt]].key < t[rt[cnt - ]].key) {
cnt --; rd[cnt] = rd[cnt + ];
rt[cnt] = merge(rt[cnt], rt[cnt + ]);
while(t[rt[cnt]].sz * > rd[cnt] - ld[cnt] + )
rt[cnt] = merge(ls(rt[cnt]), rs(rt[cnt]));
}
}
ll ans = ;
for(ri i = ; i <= cnt; i ++)
for(ri j = ld[i]; j <= rd[i]; j ++)
ans += abs(t[rt[i]].key - a[j]);
printf("%lld\n", ans);
for(ri i = ; i <= cnt; i ++)
for(ri j = ld[i]; j <= rd[i]; j ++)
printf("%d ", t[rt[i]].key + j);
return ;
}