日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

P4331 [BalticOI 2004]Sequence 数字序列(左偏树)

發布時間:2023/12/4 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P4331 [BalticOI 2004]Sequence 数字序列(左偏树) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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=1n?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_na1a2??an?,顯然有答案為bi=ab_i = abi?=a的中位數。

特殊情況一般化:

我們可以考慮把整個序列分成m,(m≤n)m, (m \leq n)m,(mn)段,對每段特殊地考慮,有區間長度為一的時候,是符合第一個條件的可把區間答案設置為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; }

總結

以上是生活随笔為你收集整理的P4331 [BalticOI 2004]Sequence 数字序列(左偏树)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。