[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[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\n", 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; }