左偏树总结

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
再讲一个题
一个链接...

posted @ 2018-01-01 21:54  租酥雨  阅读(2067)  评论(2编辑  收藏  举报