1. 程式人生 > >可並堆試水--BZOJ1367: [Baltic2004]sequence

可並堆試水--BZOJ1367: [Baltic2004]sequence

efi ide print sequence 否則 lose tar href -i

n<=1e6個數,把他們修改成遞增序列需把每個數增加或減少的總量最小是多少?

方法一:可以證明最後修改的每個數一定是原序列中的數!於是$n^2$DP(逃)

方法二:把$A_i$改成$A_i-i$,變論文題:論文

大概證明是這樣的:考慮合並兩個區間的答案,假如一個區間答案是{u,u,u,……,u},另一個是{v,v,v,……,v},那合並之後,如果u<=v最優就{u,u,……,u,v,……,v};如果u>v,假設最優是

{b1,b2,……,bn,bn+1,……,bm},那麽一定有bn<=u,否則把前半部分改成{u,u,……,u}不會更差。同理bn+1>=v。

這裏有個不懂的地方:

因此可以把他改成{bn,bn,……,bn,bn+1,……,bn+1},不會更差。可以用幾何意義感性理解一下:左邊離u越近越優,右邊離v越近越優。

然後由於bn<=u,bn+1>=v,本著“bn能大就大,bn+1能小就小”的原則讓bn=bn+1。於是合並後的最優解為{w,w,w,……,w},最優的w是誰呢?肯定是整個區間的中位數啦。

然後就可以可並堆做一波合並了,因為這裏合並後中位數只會變小,可以維護一個區間的一半小的數或一半大的數,合並兩個區間時,如果兩個區間大小都是奇數,則堆裏會多出一個數,刪之。

可並堆常需合並特定點所在的堆,因此常與並查集連用。千萬別並查集懵逼了!!因為並查集操作失誤調了一晚上。。

技術分享圖片
 1 #include<string.h>
 2 #include<stdlib.h>
 3 #include<stdio.h>
 4 #include<math.h>
 5 //#include<assert.h>
 6 #include<algorithm>
 7 //#include<iostream>
 8 using namespace std;
 9 
10 int n;
11 #define maxn 1000011
12 int root[maxn];
13 int find(int x) {return x==root[x]?x:(root[x]=find(root[x]));}
14 struct leftist 15 { 16 struct Node 17 { 18 int v,ls,rs,dis,size; 19 }a[maxn]; 20 int size; 21 leftist() {a[0].dis=-1;} 22 int merge(int x,int y) 23 { 24 if (!x || !y) return x^y; 25 if (a[x].v<a[y].v) {int t=x; x=y; y=t;} 26 a[x].rs=merge(a[x].rs,y); 27 if (a[a[x].ls].dis<a[a[x].rs].dis) {int t=a[x].ls; a[x].ls=a[x].rs; a[x].rs=t;} 28 a[x].dis=a[a[x].rs].dis+1; 29 a[x].size=a[a[x].ls].size+a[a[x].rs].size+1; 30 return x; 31 } 32 void push(int x,int &root,int val) 33 { 34 a[x].v=val; a[x].ls=a[x].rs=a[x].dis=0; a[x].size=1; 35 root=merge(root,x); 36 } 37 int top(int root) {return a[root].v;} 38 void pop(int &root) {root=merge(a[root].ls,a[root].rs);} 39 }q; 40 41 int a[maxn],sta[maxn],die[maxn],top; 42 int main() 43 { 44 scanf("%d",&n); 45 for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]-=i; 46 for (int i=1;i<=n;i++) 47 { 48 q.push(i,root[i],a[i]); int x,y; 49 while (top && q.top(x=find(root[sta[top]]))>q.top(y=find(root[i]))) 50 { 51 bool flag=0; 52 if (((sta[top]-sta[top-1])&1) && ((i-sta[top])&1)) flag=1; 53 root[x]=root[y]=q.merge(x,y); x=root[x]; 54 if (flag) die[x]=1,q.pop(root[x]),root[root[x]]=root[x]; 55 top--; 56 } 57 sta[++top]=i; 58 } 59 #define LL long long 60 LL ans=0; 61 for (int i=1;i<=n;i++) ans+=fabs(a[i]-q.top(find(root[i]))); 62 printf("%lld\n",ans); 63 return 0; 64 }
View Code

可並堆試水--BZOJ1367: [Baltic2004]sequence