BZOJ 1367 [Baltic2004]sequence (可並堆)

題面: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 }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章