数据结构:左偏树

一般的二叉堆支持的操作有插入任意值并调整堆,删除最小值并调整堆,调整堆之后我们可以O(1)取得最小的结点,这是相对于小根堆而言的

这里补充说明一下优先队列不能完成但是二叉堆可以完成的事情,那就是删除指定位置元素的值,优先队列只允许弹出堆顶元素

但是一般的二叉堆可以删除任意元素

1 inline void remove(int p) 
2 {
3     q[p]=q[n--]; 
4     up(p),down(p); 
5 }

这是对之前介绍的手写堆的一个补充

对于STL堆而言,调整元素位置之后重新建堆就好了

然后如果要合并两个堆,如果是普通的二叉堆的话,只能够暴力重构

时间复杂度O(n),可并堆是这样的一类堆,它们在将两个堆合并的时候至多是O(logn)的时间复杂度

然后我们接触其中的一种,性价比最高的一种,左偏树

性质不再描述,这里贴一张图,意会即可

然后看一道BZOJ1367

怎么想出来的我是不会的,但是能够看懂这个解法已经很可以了

首先考虑一个情况,如果t序列本身是一个递增的,那么z序列和它一模一样答案就很显然了

如果t序列是一个递减的,那么当我们知道了这个序列的中位数之后,用t序列中的每一个数减去这个中位数,所得的结果一定是最小的

可以这样考虑问题,我们把t序列分段,对于每一段维护其区间中位数

每当新加进来一个数,就形成单独的一个段

然后比较这两段的中位数,如果前一段的中位数比后一段的中位数要小,满足题意

如果前一段的中位数大于后一段的中位数的话,就要把这两个段合并

然后介绍一下如何使用堆来维护区间中位数

维护一个大根堆,堆里的元素个数等于区间长度的一半,里面保存的数是区间中较小的一半数。那么很显然中位数就是堆顶元素

在问题的最后有一个细节处理

这样求出来的z[i]是单调不减,并不保证单调递增

开始的时候将每一个t[i]减去i,然后按照该方法计算z[i],这样就相当于所有z[i]都加上了i,就把不减转化成了递增

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=1000005;
 6 int n,now;
 7 int a[maxn],root[maxn],L[maxn],R[maxn],tot[maxn];
 8 long long ans; 
 9 inline int read()
10 {
11     int x=0,f=1;char ch=getchar();
12     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
13     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
14     return x*f;
15 }
16 struct Ltree
17 {
18     int cnt;
19     int l[maxn],r[maxn];
20     int v[maxn],siz[maxn],d[maxn];
21     int merge(int x,int y)
22     {
23         if(x==0||y==0) return x+y;
24         if(v[x]<v[y]) swap(x,y);
25         r[x]=merge(r[x],y);
26         siz[x]=siz[l[x]]+siz[r[x]]+1;
27         if(d[r[x]]>d[l[x]]) swap(l[x],r[x]);
28         d[x]=d[r[x]]+1;
29         return x;
30     }
31     int top(int x)
32     {
33         return v[x];
34     }
35     int size(int x)
36     {
37         return siz[x];
38     }
39     void pop(int &x)
40     {
41         x=merge(l[x],r[x]);
42     }
43     int new_heap(int x)
44     {
45         v[++cnt]=x;
46         siz[cnt]=1;
47         l[cnt]=r[cnt]=d[cnt]=0;
48         return cnt;
49     }
50 }heap;
51 int main()
52 {
53     n=read();
54     for(int i=1;i<=n;i++) a[i]=read()-i;
55     for(int i=1;i<=n;i++)
56     {
57         now++;
58         root[now]=heap.new_heap(a[i]);
59         tot[now]=1;
60         L[now]=R[now]=i;
61         while(now>1&&heap.top(root[now-1])>heap.top(root[now]))
62         {
63             now--;
64             root[now]=heap.merge(root[now],root[now+1]);
65             tot[now]+=tot[now+1];R[now]=R[now+1];
66             while(heap.size(root[now])*2>tot[now]+1)
67                 heap.pop(root[now]);
68         }
69     }
70     for(int i=1;i<=now;i++)
71         for(int j=L[i],t=heap.top(root[i]);j<=R[i];j++)
72             ans+=abs(a[j]-t);
73     printf("%lld",ans);
74     return 0;
75 }
原文地址:https://www.cnblogs.com/aininot260/p/9524530.html