1. 程式人生 > >BZOJ 1367 [Baltic2004]sequence (可並堆)

BZOJ 1367 [Baltic2004]sequence (可並堆)

zoj struct 題目 com namespace += def fine 一個

題面:BZOJ傳送門

題目大意:給你一個序列$a$,讓你構造一個遞增序列$b$,使得$\sum |a_{i}-b_{i}|$最小,$a_{i},b_{i}$均為整數

神仙題..

我們先考慮b不遞減的情況

假設現在有一段單調的序列$A$

如果$A$是遞增的,顯然$b[i]=a[i]$是最優解

如果$A$是遞減的,$b$的每一項=序列$A$的中位數時是最優解

簡單證明一下遞減的情況:

1.序列$A$元素數量是奇數時,我們以中位數為對稱軸,那麽對稱的兩個數帶來的貢獻就是它們的差值,而中位數本身不會產生貢獻,如果選取的不是中位數,必然會導致中位數產生貢獻,而且對稱的兩個數還可能產生差值以外的貢獻

2.序列$A$元素數量是偶數時,選取的數在中間的兩個數之間即可,貢獻都是一樣的

我們如何利用這一性質呢?

我們把序列拆分成很多遞減的段,遞增子段的每一個數都是單獨的一段

我們的目的是保證$b$不遞減,即把每一段取的數畫成一個函數來看是不遞減的

每次我們在序列末尾加入一個數$a_{i}$,都看看這一段的中位數是否$\geq $前面一段的中位數,不滿足就和前一段合並,然後依次重復此過程

為什麽答案合並後不會變差?太弱了並不會證..感性理解一下吧。因為這兩段原來取的就是最優解,但並不滿足要求,我們也只能把兩段合並,再用相同的方法求最優解了..

具體實現可以用左偏樹維護大根堆,記錄每一段較大的$\left \lceil \frac{n}{2} \right \rceil$個數,當兩個奇數段合並時刪除一次堆頂即可,記錄每一段的信息可以用結構體

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #define N1 1000010
 6 #define ll long long 
 7 using namespace std;
 8 const ll inf=0x3f3f3f3f3f3f3f3fll;
 9  
10 template <typename _T> void read(_T &ret)
11 {
12     ret=0
; _T fh=1; char c=getchar(); 13 while(c<0||c>9){ if(c==-) fh=-1; c=getchar(); } 14 while(c>=0&&c<=9){ ret=ret*10+c-0; c=getchar(); } 15 ret=ret*fh; 16 } 17 18 struct Heap{ 19 int fa[N1],ch[N1][2],h[N1]; ll val[N1]; 20 int merge(int x,int y) 21 { 22 if(!x||!y) return x+y; 23 if(val[x]<val[y]) swap(x,y); 24 //pushdown(x); 25 ch[x][1]=merge(ch[x][1],y); fa[ch[x][1]]=x; 26 if(h[ch[x][0]]<h[ch[x][1]]) swap(ch[x][0],ch[x][1]); 27 h[x]=h[ch[x][1]]+1; 28 return x; 29 } 30 int del(int x) 31 { 32 fa[ch[x][0]]=fa[ch[x][1]]=0; 33 int y=merge(ch[x][0],ch[x][1]); 34 ch[x][0]=ch[x][1]=0; 35 return y; 36 } 37 }h; 38 39 ll a[N1]; 40 struct node{int l,r,x;}; 41 node stk[N1]; int n,tp; 42 43 int main() 44 { 45 scanf("%d",&n); 46 int i,j,x,y,len; node K; ll ans=0,tmp; 47 for(i=1;i<=n;i++) read(a[i]); 48 for(i=1;i<=n;i++) 49 { 50 h.val[i]=a[i]-i; x=i; len=1; 51 while(tp) 52 { 53 K=stk[tp]; 54 if(h.val[x]>=h.val[K.x]) break; 55 x=h.merge(x,K.x); tp--; 56 if( (len&1) && ((K.r-K.l+1)&1) ) x=h.del(x); 57 len+=K.r-K.l+1; 58 } 59 stk[++tp]=(node){i-len+1,i,x}; 60 } 61 for(i=1;i<=tp;i++) 62 for(j=stk[i].l;j<=stk[i].r;j++) 63 { 64 tmp=a[j]-(h.val[stk[i].x]+j); 65 ans+=((tmp>0)?tmp:-tmp); 66 } 67 printf("%lld\n",ans); 68 return 0; 69 }

BZOJ 1367 [Baltic2004]sequence (可並堆)