• 左偏树学习笔记


    今天刚刚学习了左偏树,这里记录一下,加深理解

    首先,我们用左偏树可以做到(O(log_n))来合并两个堆,(O(log_n))来删除等一些(O(log_n))的操作

    (dis)为这个节点到它的子树中最近的一个叶子的距离,对于一个小根堆它有这么几条性质

    1.一个节点的值小与左、右儿子的值
    2.一个节点的左儿子的(dis)不小于右儿子的(dis)
    3.一个节点的(dis)始终等于右儿子+1
    4.节点数为n的左偏树,距离最大为(log_{(n+1)}−1)

    然后来写一点它的基本的操作

    (Merge)合并堆

    每次递归的(x)(y)就是两个堆的根节点,首先我们要让(y)的值更大来方便后续的操作,判一下是不是(y)更大,不是就(swap)一下,然后我们就可以考虑让(y)所在的堆来与(x)的右子树合并,然后一直递归,最后把(y)接到(x)的右子树上

    那么怎么维护左偏呢?每次判一下(x)的左右两个儿子,如果右儿子的(dis)大于左儿子的,那么交换这两个儿子

    int merge(int xx,int yy)
    {
    	if(xx==0||yy==0)
    		return max(xx,yy);
    	if(a[xx].num>a[yy].num||(a[xx].num==a[yy].num&&xx>yy))
    		swap(xx,yy);
    	a[xx].r1=merge(a[xx].r1,yy);
    	if(a[a[xx].l1].dis<a[a[xx].r1].dis)
    		swap(a[xx].l1,a[xx].r1);
    	a[xx].dis=a[a[xx].r1].dis+1,a[a[xx].r1].fa=a[a[xx].l1].fa=a[xx].fa=xx;
    	return xx;
    }
    

    Pop删除所在堆中最小结点

    这个就只接把堆顶的左右两个子树(Merge)一下即可

    但是有一个问题让我在这里想了很久,代码调了好久才发现是这里的问题,这也是我写这篇博客的理由QWQ

    a[xx].fa=merge(a[xx].l1,a[xx].r1);
    

    如果你是写的路径压缩的并查集,那么就要把删去的点的父亲设为他的两个子树合并后的堆顶

    那么这是为什么呢?如上图所示,我们删去了1号节点,而由于路径压缩的原因,下面的2、3、4、5号节点的父亲仍为1号节点,如果不把1号节点的父亲设为2号节点,那么在查找下面节点的堆顶的时候就会找到1号节点这个已经被删掉的节点

    void pop(int xx)
    {
    	a[xx].num=-1,a[a[xx].l1].fa=a[xx].l1,a[a[xx].r1].fa=a[xx].r1;
    	a[xx].fa=merge(a[xx].l1,a[xx].r1);
    }
    

    还有一些其他的操作,要注意一下维护左偏树的性质,具体就不在这写了

    洛谷模板的代码

    #include<bits/stdc++.h>
    using namespace std;
    struct node
    {
    	int num,l1,r1,fa,dis;
    }a[100003];
    int n,m,typ,x,y;
    int get(int x1)
    {
    	while(a[x1].fa!=x1)
    		a[x1].fa=a[a[x1].fa].fa,x1=a[x1].fa;
    	return x1;
    }
    int merge(int xx,int yy)
    {
    	if(xx==0||yy==0)
    		return max(xx,yy);
    	if(a[xx].num>a[yy].num||(a[xx].num==a[yy].num&&xx>yy))
    		swap(xx,yy);
    	a[xx].r1=merge(a[xx].r1,yy);
    	if(a[a[xx].l1].dis<a[a[xx].r1].dis)
    		swap(a[xx].l1,a[xx].r1);
    	a[xx].dis=a[a[xx].r1].dis+1,a[a[xx].r1].fa=a[a[xx].l1].fa=a[xx].fa=xx;
    	return xx;
    }
    void pop(int xx)
    {
    	a[xx].num=-1,a[a[xx].l1].fa=a[xx].l1,a[a[xx].r1].fa=a[xx].r1;
    	a[xx].fa=merge(a[xx].l1,a[xx].r1);
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	a[0].dis=-1;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		a[i].fa=i;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&typ,&x);
    		if(typ==1)
    		{
    			scanf("%d",&y);
    			if(a[x].num==-1||a[y].num==-1)
    				continue;
    			x=get(x),y=get(y);
    			if(x!=y)
    				a[x].fa=a[y].fa=merge(x,y);
    		}
    		else
    		{
    			if(a[x].num==-1)
    			{
    				cout<<-1<<endl;
    				continue;
    			}
    			x=get(x);
    			cout<<a[x].num<<endl;
    			pop(x);
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Linux 提权-依赖 Exp 篇
    s-cms学校建站重装漏洞
    极致CMS建站系统后台GETSHELL
    泛微weaver_oa filebrowser.jsp 任意目录遍历
    泛微oa系统com.eweaver.base.DataAction文件sql参数sql注入
    Supervisord rce(CVE-2017-11610)
    docker安装
    互联网测试开发面试题集锦【转】
    测试面试常见面试题汇总一
    Python操作MongoDB文档数据库
  • 原文地址:https://www.cnblogs.com/dzice/p/12270134.html
Copyright © 2020-2023  润新知