可并堆--左偏树(Bzoj1367&&Bzoj1455)
来源:互联网 发布:手机追踪软件 编辑:程序博客网 时间:2024/06/07 18:04
今天为了学习可持久化的堆恶补了一下左偏树
左偏树是向左偏的(废话)
所以其实我们也可以写个右偏树QwQ
左偏树除了维护一个键值外还维护了一个距离属性d
我们再定义一个外节点为存在一颗子树为空的节点。
我们定义每个节点的距离属性dis为当前节点到最近一个外子节点的路径长度。
特别地,我们对于一个空节点,他的dis=-1。
有性质:dis[leftson[now]]>=dis[rightson[now]],这保证了左偏性质
那么我们可以计算dis[now]=dis[rightson[now]]+1;
既然是可并堆,合并是少不了的。
假设我们正在维护一个小根堆,那么键值大的往键值小的的堆里插即可。
插完后会发现左偏树的性质可能不满足,此时我们只需要交换左右子树来满足即可。
int merge(int a,int b){ if(a==0||b==0)return a+b; if(T[a].v<T[b].v)swap(a,b); T[a].r=merge(T[a].r,b); if(T[T[a].l].d<T[T[a].r].d)swap(T[a].l,T[a].r); T[a].d=T[a].r?T[T[a].r].d+1:0; return a;}
左偏树最重要的操作就是合并,删除堆顶就是合并左右子树
例题:题目链接:BZOJ1367[Baltic2004]sequence
解法在黄源河前辈的论文里有
就是利用可并堆维护中位数,这里用左偏树维护
我们知道一段子序列的最优解一定有一个是中位数
所以我们一段一段地合并中位数并使其符合单调性质
可并堆维护中位数的做法是序列前⌈n/2⌉小的数。
我们用大根堆维护,堆顶即是中位数
由于我们是从大小为1的堆开始合并的所以不用考虑排序,堆自带有序QAQ
但是如果两个堆大小都是奇数的时候我们合并堆后的堆的元素个数会比⌈n/2⌉多1
所以这时候我们要删除堆顶,即直接合并根的左右子树
这样做下去我们会将原数列分成一段段区间,一个区间有一个中位数
代码:
#include<cstdio>#include<vector>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#define LL long longusing namespace std;const int maxn=1000000+10;struct LeftIstTree{ struct treenode{ int v,l,r,d; treenode(){d=l=r=v=0;} bool operator<(const treenode&a)const{return v<a.v;} }; treenode T[maxn]; int merge(int a,int b){ if(a==0||b==0)return a+b; if(T[a]<T[b])swap(a,b); T[a].r=merge(T[a].r,b); if(T[T[a].l].d<T[T[a].r].d)swap(T[a].l,T[a].r); T[a].d=T[a].r?T[T[a].r].d+1:0; return a; } int root[maxn],m,size[maxn]; void solve(int n){ T[0].d=-1; for(int i=1;i<=n;i++){ scanf("%d",&T[i].v); T[i].v-=i; root[++m]=i; size[m]=1; while(m>1&&T[root[m]].v<T[root[m-1]].v){ int k=size[m]&size[m-1]&1; root[m-1]==merge(root[m-1],root[m]); if(k)root[m-1]=merge(T[root[m-1]].l,T[root[m-1]].r); size[m-1]+=size[m]; m--; } } int cur=1; LL ans=0; for(int i=1;i<=m;i++) for(int j=1;j<=size[i];j++,cur++)ans+=abs(T[root[i]].v-T[cur].v); printf("%lld\n",ans); }}Left_Ist_Tree;int main(){ int n; scanf("%d",&n); Left_Ist_Tree.solve(n);}
例题:Bzoj1455: 罗马游戏
左偏树裸题,注意杀人的时候不要把被杀的人扔出堆
#include<cstdio>#include<vector>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn=2000000+10;int n,m,f[maxn],a[maxn];bool Died[maxn];int find(int x){ return x==f[x]?x:f[x]=find(f[x]);}struct Node{ int d,v,l,r; Node(){d=l=r=v=0;}}T[maxn];int merge(int a,int b){ if (!a||!b) return a+b; if (T[a].v>T[b].v) swap(a,b); T[a].r=merge(T[a].r,b); if (T[T[a].l].d<T[T[a].r].d) swap(T[a].l,T[a].r); T[a].d=T[a].r?T[T[a].r].d+1:0; return a;}int main(){ scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d",&a[i]),f[i]=i,T[i].v=a[i]; scanf("%d",&m); T[0].d=-1; int x,y; char str[10]; for (int i=1;i<=m;++i){ scanf("%s",str); if (str[0]=='M'){ scanf("%d%d",&x,&y); if (Died[x]||Died[y]) continue; int p=find(x),q=find(y); if (p!=q){ int t=merge(p,q); f[p]=t; f[q]=t; } }else{ scanf("%d",&x);int p=find(x); if (Died[x]){printf("0\n");continue;} printf("%d\n",T[p].v); Died[p]=1; int t=merge(T[p].l,T[p].r); f[p]=t; f[t]=t; } }}
- 可并堆--左偏树(Bzoj1367&&Bzoj1455)
- [ 可并堆 贪心 ] [ Baltic2004 ] BZOJ1367
- bzoj1455 罗马游戏(可并堆-左偏树)
- 【BZOJ1455】罗马游戏 可并堆
- [BZOJ1455]罗马游戏(可并堆)
- bzoj1367 [Baltic2004]sequence(可并堆+中位数)
- bzoj1455 罗马游戏(左偏树(可并堆)+ 并查集)
- bzoj1455 罗马游戏【并查集+可并堆】
- bzoj1455 罗马游戏(可并堆模板)
- 可并堆?左偏树?
- 【bzoj1367】左偏树
- 左偏树(可并堆)
- HDU1512 左偏树(可并堆)
- 可并堆之左偏树
- 【左偏树(可并堆)模板】
- [BZOJ1455]罗马游戏 左偏树+并查集
- 左偏树/斜堆/可并堆-洛谷P3377 【模板】左偏树(可并堆)
- 可并堆之左偏树总结
- LEEDCODE 14 Longest Common Prefix (JAVA题解)
- 英语学习: The Greatest Law of Life
- hdu 5631 Rikka with Graph【并查集+判断一个祖先+思维】
- linux 下SVN服务端创建版本库
- react-native启动异常,react-deep-force-update/.babelrc
- 可并堆--左偏树(Bzoj1367&&Bzoj1455)
- 对MySQL加锁的初步理解(一)
- [Python]学习Celery
- iOS 播放器思路
- C基础——itoa
- 终端中文件访问命令
- Android判断屏幕状态与屏幕解锁和锁定
- 深入分析 Java I/O 的工作机制
- ubuntu14.04LTS更新源