左偏树/斜堆/可并堆-洛谷P3377 【模板】左偏树(可并堆)
https://www.luogu.org/problem/show?pid=3377
我们知道二叉堆,就是优先队列,那个stl有的,Priority Queues;
这个建议大家去试试手写;
我们知道二叉堆合并很难,时间复杂度高;
那我们怎么搞呢,用更高级的数据结构–可并堆;
先自学一下这个
http://wenku.baidu.com/search?word=%CB%E3%B7%A8%BA%CF%BC%AF%D6%AE%A1%B6%D7%F3%C6%AB%CA%F7%B5%C4%CC%D8%B5%E3%BC%B0%C6%E4%D3%A6%D3%C3%A1%B7&lm=0&od=0&fr=top_home
然后我就没什么好讲的了;
……
但是有些想法还是跟大家说说;
首先我们为什么要让这个堆左边偏大;
这个跟我们合并有关系;
合并时,我们把权值小的根节点的右儿子和权值大的更节点先合并;
所以我们让右边小一点合并速度更快;
对吧(对个屁)
那我们为什么要让 《权值小的根节点的右儿子去合并
而不是 《值大的根节点的右儿子去合并呢?;
因为根节点要最小呀!
那我们为什么是右节点合并而不是左合并,这样右节点深度会更小;
我们让右节点深度小,就是为了让右节点去合并,这样时间复杂度才可以保证啊;
那这时模版代码;
但是我的查询时间是O(log2n);
毕竟我们要找到根节点嘛;
当然可以用路径压缩;
这个是斜对
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
struct heap{
int xx,yy,vv,fa,;
}a[100001];
bool b[100001];
int n,m,x,y,z;
int merge(int x,int y){
if(!x)return y;
if(!y)return x;
if((a[x].vv>a[y].vv)||(a[x].vv==a[y].vv&&x>y))swap(x,y);
int f=merge(a[x].yy,y);
a[x].yy=a[x].xx;
a[x].xx=f;
return x;
}
void dfs(int x,int y){
if(!x)return;
a[x].fa=y;
dfs(a[x].xx,y);
dfs(a[x].yy,y);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i].vv),a[i].fa=i;
while(m--){
scanf("%d",&z);
if(z==1){
scanf("%d%d",&x,&y);
if(b[x]||b[y])continue;
int xx=a[x].fa;
int yy=a[y].fa;
if(xx==yy)continue;
int f=merge(xx,yy);
dfs(f,f);
}else{
scanf("%d",&x);
if(b[x]){printf("-1\n");continue;}
int xx=a[x].fa;
b[xx]=1;
printf("%d\n",a[xx].vv);
int f=merge(a[xx].xx,a[xx].yy);
dfs(f,f);
}
}
}
左偏树(跑得慢??????)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
struct heap{
int xx,yy,vv,fa,dist;
}a[100001];
bool b[100001];
int n,m,x,y,z;
int merge(int x,int y){
if(!x)return y;
if(!y)return x;
if((a[x].vv>a[y].vv)||(a[x].vv==a[y].vv&&x>y))swap(x,y);
int f=merge(a[x].yy,y);
a[x].yy=f;
if(a[a[x].yy].dist>a[a[x].xx].dist)swap(a[x].xx,a[x].yy);
a[x].dist=a[a[x].yy].dist+1;
return x;
}
void dfs(int x,int y){
if(!x)return;
a[x].fa=y;
dfs(a[x].xx,y);
dfs(a[x].yy,y);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i].vv),a[i].fa=i;
while(m--){
scanf("%d",&z);
if(z==1){
scanf("%d%d",&x,&y);
if(b[x]||b[y])continue;
int xx=a[x].fa;
int yy=a[y].fa;
if(xx==yy)continue;
int f=merge(xx,yy);
dfs(f,f);
}else{
scanf("%d",&x);
if(b[x]){printf("-1\n");continue;}
int xx=a[x].fa;
b[xx]=1;
printf("%d\n",a[xx].vv);
int f=merge(a[xx].xx,a[xx].yy);
dfs(f,f);
}
}
}
路径压缩(无用,慢)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
struct heap{
int xx,yy,vv,fa;
}a[100001];
bool b[100001];
int n,m,x,y,z;
int merge(int x,int y){
if(!x)return y;
if(!y)return x;
if((a[x].vv>a[y].vv)||(a[x].vv==a[y].vv&&x>y))swap(x,y);
a[x].yy=merge(a[x].yy,y);
a[a[x].yy].fa=x;
swap(a[x].yy,a[x].xx);
return x;
}
int get(int x){while(a[x].fa!=x)x=a[x].fa;return x;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i].vv),a[i].fa=i;
while(m--){
scanf("%d",&z);
if(z==1){
scanf("%d%d",&x,&y);
if(b[x]||b[y])continue;
int xx=get(x);
int yy=get(y);
if(xx==yy)continue;
int f=merge(xx,yy);
}else{
scanf("%d",&x);
if(b[x]){printf("-1\n");continue;}
int xx=get(x);
b[xx]=1;
printf("%d\n",a[xx].vv);
a[xx].fa=merge(a[xx].xx,a[xx].yy);
a[a[xx].fa].fa=a[xx].fa;
}
}
}