P4331 [BalticOI 2004]Sequence 数字序列
给定一个序列整数a1,a2,a3,…,an−1,ana_1, a_2, a_3, \dots, a_{n - 1}, a_na1,a2,a3,…,an−1,an,要找一个整数序列bbb,满足b1<b2<b3<⋯<bn−1<bnb_1 < b_2 < b_3< \dots< b_{n - 1}< b_{n}b1<b2<b3<⋯<bn−1<bn,使∑i=1n∣ai−bi∣\sum\limits_{i = 1} ^{n} \mid a_i - b_i \midi=1∑n∣ai−bi∣最小。
先对aia_iai整体减去iii,也就是ai=ai−ia_i = a_i - iai=ai−i,最后我们再对答案加上对应的做标,这个问题就变成找一个非递减序列了,b1≤b2≤⋯≤bnb_1 \leq b_2 \leq \dots \leq b_{n}b1≤b2≤⋯≤bn。
考虑两种特殊情况:
- ① a1≤a2≤⋯≤ana_1 \leq a_2 \leq \dots \leq a_na1≤a2≤⋯≤an,显然有答案为bi=aib_i = a_ibi=ai。
- ② a1≥a2≥⋯≥ana1 \geq a_2 \geq \dots \geq a_na1≥a2≥⋯≥an,显然有答案为bi=ab_i = abi=a的中位数。
特殊情况一般化:
我们可以考虑把整个序列分成m,(m≤n)m, (m \leq n)m,(m≤n)段,对每段特殊地考虑,有区间长度为一的时候,是符合第一个条件的可把区间答案设置为bib_ibi。
对于相邻的区间,如果满足ans[i−1]≤ans[i]ans[i - 1] \leq ans[i]ans[i−1]≤ans[i],显然我们可以不做改变,即这就是一个合法的且最优的答案。
如果相邻的区间存在ans[i−1]≥ans[i]ans[i - 1] \geq ans[i]ans[i−1]≥ans[i],那么我们就要对这两个区间做一定的变化了,由②可知,我们选这两段区间的中位数可使改变后的答案也是最优的。
根据上述可知,我们需要快速合并一段区间,并且需要快速求出这段合并的区间的中位数,考虑用可并堆实现即可。
#include <bits/stdc++.h>using namespace std;const int N = 1e6 + 10;int ls[N], rs[N], value[N], dis[N];int root[N], sz[N], l[N], r[N], ans[N], n, cnt;int merge(int x, int y) {if (!x || !y) {return x | y;}if (value[x] < value[y]) {swap(x, y);}rs[x] = merge(rs[x], y);if (dis[ls[x]] < dis[rs[x]]) {swap(ls[x], rs[x]);}dis[x] = dis[rs[x]] + 1;return x;
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%d", &value[i]);value[i] -= i;}for (int i = 1; i <= n; i++) {cnt++;root[cnt] = i, l[cnt] = i, r[cnt] = i, sz[cnt] = 1, ans[cnt] = value[i];while (cnt > 1 && ans[cnt - 1] > ans[cnt]) {cnt--;root[cnt] = merge(root[cnt], root[cnt + 1]);sz[cnt] += sz[cnt + 1];r[cnt] = r[cnt + 1];while (sz[cnt] > (r[cnt] - l[cnt] + 3) / 2) {sz[cnt]--;root[cnt] = merge(ls[root[cnt]], rs[root[cnt]]);}ans[cnt] = value[root[cnt]];}}long long res = 0;for (int i = 1; i <= cnt; i++) {for (int j = l[i]; j <= r[i]; j++) {res += abs(value[j] - ans[i]);}}printf("%lld\n", res);for (int i = 1; i <= cnt; i++) {for (int j = l[i]; j <= r[i]; j++) {printf("%d%c", ans[i] + j, j == n ? '\n' : ' ');}}return 0;
}