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