zoukankan      html  css  js  c++  java
  • 左偏树总结

    Part 1

    问:左偏树是啥?
    答:就是往左偏的树。
    评:废话。
    追答:其实是具有堆性质的,同时也具有左偏性质的树啦。
    追评:讲的很厉害的样子,但还是废话啊。

    Part 2

    左偏树的堆性质不需要解释,就是说这是一棵二叉树,而且父亲节点的键值要比左右儿子(如果有的话)都大(为方便表述,以下堆均指的是大根堆)。至于左偏性质,自然就是为了实现快速合并的。
    我们把一个东西记作(dis),空节点默认(dis=0),非空节点为(dis_i=min(dis_{ls},dis_{rs})+1)(就是说左右儿子里面取较小)。要维护左偏的性质,就要保证(dis_{ls}>=dis_{rs}),这样一来,就有(dis_i=dis_{rs}+1)
    在我们合并两个堆的时候,我们考虑哪个堆的堆顶元素会作为新堆的堆顶元素。显然是键值较大的那一个。那么我们就,把较大的作为堆顶,然后,把另一个堆跟堆顶的右儿子进行合并!合并的过程是递归的,可以证明复杂度为(O(logn))
    所以说这样就可以实现快速合并了嗯哼。
    放一个合并(Merge)的代码。

    int Merge(int A,int B){
    	if (!A||!B) return A+B;
    	if (key[A]<key[B]) swap(A,B);
    	rs[A]=Merge(rs[A],B);
    	if (dis[ls[A]]<dis[rs[A]]) swap(ls[A],rs[A]);
    	dis[A]=dis[rs[A]]+1;
    	return A;
    }
    

    删除堆顶元素就很简单了。直接把左右儿子合并就好了。

    int Delete(int A){
    	return Merge(ls[A],rs[A]);
    }
    

    Part 3

    放一个模板题

    [APOI2012]派遣dispatching

    BZOJ
    Luogu
    在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。在这个帮派里,有一名忍者被称之为(Master)。除了 (Master)以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者 发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递 人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,就不需要支付管理者的薪水。你的目标是在预算内使顾客的满意度最大。这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。写一个程序,给定每一个忍者(i)的上级 (B_i),薪水(C_i),领导力(L_i),以及支付给忍者们的薪水总预算 (M),输出在预算内满足上述要求时顾客满意度的最大值。

    sol

    我们希望能够算出以每一个忍者作为管理者的时候的最优答案。显然,在一棵子树中,我们希望保留尽可能多的且总权值和不大于(M)的节点,必将会贪心地逐个丢掉最大值。所以我们用左偏树维护大根堆,先把所有子树的大根堆合并,再依次弹出最大值以使得总权值和不大于(M)
    注意到算法的正确性:在某一棵子树中被弹掉了,这个节点将不会再出现在更上方祖先的最优答案中。
    算法复杂度:考虑到每个节点都会被插入和删除至多一次,所以复杂是(O(nlogn))

    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int N = 100005;
    struct edge{int to,next;}a[N];
    int head[N],cnt;
    int n,Master,ls[N],rs[N],dis[N];
    ll m,C[N],L[N],sum[N],sz[N],ans;
    ll gi()
    {
        ll x=0,w=1;char ch=getchar();
        while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if (ch=='-') w=0,ch=getchar();
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return w?x:-x;
    }
    void Link(int u,int v)
    {
        a[++cnt]=(edge){v,head[u]};
        head[u]=cnt;
    }
    int Merge(int A,int B)
    {
        if (!A||!B) return A+B;
        if (C[A]<C[B]) swap(A,B);
        rs[A]=Merge(rs[A],B);
        if (dis[ls[A]]<dis[rs[A]]) swap(ls[A],rs[A]);
        dis[A]=dis[rs[A]]+1;
        return A;
    }
    int Delete(int A)
    {
        return Merge(ls[A],rs[A]);
    }
    int dfs(int u)
    {
        int A=u,B;
        sum[u]=C[u];sz[u]=1;
        for (int e=head[u];e;e=a[e].next)
        {
            int v=a[e].to;
            B=dfs(v);
            A=Merge(A,B);
            sum[u]+=sum[v];sz[u]+=sz[v];
        }
        while (sum[u]>m)
        {
            sum[u]-=C[A];sz[u]--;
            A=Delete(A);
        }
        ans=max(ans,L[u]*sz[u]);
        return A;
    }
    int main()
    {
        n=gi();m=gi();
        for (int i=1;i<=n;i++)
        {
            int u=gi();
            if (!u) Master=i;
            else Link(u,i);
            C[i]=gi();L[i]=gi();
        }
        dfs(Master);
        printf("%lld",ans);
        return 0;
    }
    
    

    Part 4
    再讲一个题
    一个链接...

  • 相关阅读:
    Golang 函数
    Golang type
    Golang 分支控制和循环
    Golang 字符型
    Golang运算符
    final 和 static 的区别
    Golang标识符命名规则
    Golang值类型与引用类型
    Golang指针
    启动 jvm 参数小总结
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8169074.html
Copyright © 2011-2022 走看看