P4331-[BalticOI2004]Sequence数字序列【左偏树】
生活随笔
收集整理的這篇文章主要介紹了
P4331-[BalticOI2004]Sequence数字序列【左偏树】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
正題
題目鏈接:https://www.luogu.com.cn/problem/P4331
題目大意
給出一個序列aaa,求一個單調上升的序列bbb使得∑i=1n∣ai?bi∣\sum_{i=1}^n|a_i-b_i|∑i=1n?∣ai??bi?∣最小。
解題思路
巧妙的解法
首先我們讓所有的ai?ia_i-iai??i這樣我們求的bbb序列就只需要滿足單調不降。
然后考慮特殊情況
- 對于一串連續的滿足ax≤ax+1a_x\leq a_{x+1}ax?≤ax+1?那么這一段的bx=axb_x=a_xbx?=ax?
- 對于一串連續的滿足ax≥ax+1a_x\geq a_{x+1}ax?≥ax+1?那么這一段的bxb_xbx?為這一段數的中位數
而且對于連續的兩個區間(L,mid)(L,mid)(L,mid)和(mid+1,R)(mid+1,R)(mid+1,R)的局部最優解也滿足一下性質,所以就有做法:
我們維護若干段局部最優解www,每次加入一個數aia_iai?如果滿足ai≥wcnta_i\geq w_{cnt}ai?≥wcnt?那么有bi=aib_i=a_ibi?=ai?。如果ai≤wcnta_i\leq w_{cnt}ai?≤wcnt?那么我們就將aia_iai?合并入前面的局部最優解,并重新計算這一段的中位數后繼續看是否需要與前面合并(即判斷是否wcnt≥wcnt?1w_{cnt}\geq w_{cnt-1}wcnt?≥wcnt?1?)。
我們可以左偏樹做到動態合并和維護中位數(保持堆中個數為一半即可)。時間復雜度O(nlog?n)O(n\log n)O(nlogn)
codecodecode
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e6+10; int n,cnt,a[N],rt[N],L[N],R[N]; int t[N][2],dis[N],val[N],siz[N]; long long ans; int Merge(int x,int y){if(!x||!y)return x+y;if(val[x]<val[y])swap(x,y);int &ls=t[x][0],&rs=t[x][1];rs=Merge(rs,y);if(dis[ls]<dis[rs])swap(ls,rs);dis[x]=dis[ls]+1;return x; } void Pop(int x) {siz[x]--;rt[x]=Merge(t[rt[x]][0],t[rt[x]][1]);} int main() {scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]-=i;for(int i=1;i<=n;i++){siz[++cnt]=1;val[i]=a[i];rt[cnt]=i;L[cnt]=R[cnt]=i;while(cnt>1&&val[rt[cnt-1]]>val[rt[cnt]]){cnt--;siz[cnt]+=siz[cnt+1];R[cnt]=R[cnt+1];rt[cnt]=Merge(rt[cnt],rt[cnt+1]);while(siz[cnt]>(R[cnt]-L[cnt])/2+1)Pop(cnt);}}for(int i=1;i<=cnt;i++)for(int j=L[i];j<=R[i];j++)ans+=abs(a[j]-val[rt[i]]);printf("%lld\n",ans);for(int i=1;i<=cnt;i++)for(int j=L[i];j<=R[i];j++)printf("%d ",val[rt[i]]+j); }總結
以上是生活随笔為你收集整理的P4331-[BalticOI2004]Sequence数字序列【左偏树】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: P4735-最大异或和【可持久化Trie
- 下一篇: P3812-[模板]线性基