【BZOJ1367】【Baltic2004】sequence - 可合併堆

題意:

題解:

其實這是道水題啦……只不過我沒作過而已node

先考慮構造不嚴格遞增序列,考慮原序列中的一段降低區間,顯然區間中的$z$全取中位數最優;ios

那麼能夠把原序列拆成不少個降低序列,從頭至尾加入原序列中的數,每次把加進來的數當作一個新的降低區間,而後不斷合併最後兩個區間直到,最後一個區間的中位數不小於倒數第二個區間的中位數;spa

用可合併堆維護便可,左偏樹啥的都行,我寫的斜堆;code

可合併堆如何維護區間中位數?只保留較小一半的數,則堆頂就是中位數;blog

要構造嚴格遞增序列只須要把原序列中的每一個數$t_i$減去$i$便可(顯然我不會證);string

代碼:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std; 10 typedef long long ll; 11 typedef double db; 12 struct node{ 13     int ls,rs,v,siz; 14 }t[1000001]; 15 int n,cnt=0,num[1000001],rts[1000001],R[1000001]; 16 ll ans=0; 17 int merge(int x,int y){ 18     if(!x||!y)return x|y; 19     if(t[x].v<t[y].v)swap(x,y); 20     t[x].siz+=t[y].siz; 21     t[x].rs=merge(t[x].rs,y); 22  swap(t[x].ls,t[x].rs); 23     return x; 24 } 25 int main(){ 26     scanf("%d",&n); 27     for(int i=1;i<=n;i++){ 28         scanf("%d",&num[i]); 29         num[i]-=i; 30         t[i].v=num[i]; 31         t[i].siz=1; 32         cnt++; 33         rts[cnt]=R[cnt]=i; 34         while(cnt>1&&t[rts[cnt]].v<t[rts[cnt-1]].v){ 35             R[cnt-1]=R[cnt]; 36             cnt--; 37             rts[cnt]=merge(rts[cnt],rts[cnt+1]); 38             while(t[rts[cnt]].siz*2>R[cnt]-R[cnt-1]+1){ 39                 rts[cnt]=merge(t[rts[cnt]].ls,t[rts[cnt]].rs); 40  } 41  } 42  } 43     for(int i=1,j=1;i<=cnt;i++){ 44         for(;j<=R[i];j++){ 45             ans+=abs(t[rts[i]].v-num[j]); 46  } 47  } 48     printf("%lld\n",ans); 49     return 0; 50 }
相關文章
相關標籤/搜索