P4331-[BalticOI2004]Sequence数字序列【左偏树】
正題
題目鏈接:https://www.luogu.com.cn/problem/P4331
題目大意
給出一個(gè)序列aaa,求一個(gè)單調(diào)上升的序列bbb使得∑i=1n∣ai?bi∣\sum_{i=1}^n|a_i-b_i|∑i=1n?∣ai??bi?∣最小。
解題思路
巧妙的解法
首先我們讓所有的ai?ia_i-iai??i這樣我們求的bbb序列就只需要滿足單調(diào)不降。
然后考慮特殊情況
- 對(duì)于一串連續(xù)的滿足ax≤ax+1a_x\leq a_{x+1}ax?≤ax+1?那么這一段的bx=axb_x=a_xbx?=ax?
- 對(duì)于一串連續(xù)的滿足ax≥ax+1a_x\geq a_{x+1}ax?≥ax+1?那么這一段的bxb_xbx?為這一段數(shù)的中位數(shù)
而且對(duì)于連續(xù)的兩個(gè)區(qū)間(L,mid)(L,mid)(L,mid)和(mid+1,R)(mid+1,R)(mid+1,R)的局部最優(yōu)解也滿足一下性質(zhì),所以就有做法:
我們維護(hù)若干段局部最優(yōu)解www,每次加入一個(gè)數(shù)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?合并入前面的局部最優(yōu)解,并重新計(jì)算這一段的中位數(shù)后繼續(xù)看是否需要與前面合并(即判斷是否wcnt≥wcnt?1w_{cnt}\geq w_{cnt-1}wcnt?≥wcnt?1?)。
我們可以左偏樹(shù)做到動(dòng)態(tài)合并和維護(hù)中位數(shù)(保持堆中個(gè)數(shù)為一半即可)。時(shí)間復(fù)雜度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); }總結(jié)
以上是生活随笔為你收集整理的P4331-[BalticOI2004]Sequence数字序列【左偏树】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: P4735-最大异或和【可持久化Trie
- 下一篇: P3812-[模板]线性基