【算法简介】
顾名思义,这个数据结构向左偏,而且主要的性质是可以合并,而且是一个有序的堆
定义外节点是有一个儿子是空的的节点,每个点的dis定义为到最近的外节点经过的边的数量
我们让每个节点的左儿子的dis>=右儿子的dis,然后操作尽量从右侧开始,通过这种方式来保证其优秀的时间效率
合并操作就是每次让右侧儿子和另一棵树进行合并,合并的过程中注意满足有序的性质,还有左偏的性质就行
还有一个删除操作,左偏树不支持删除某个值得点的操作,只能删除某个已知位置的点,一般常用的操作是删除根节点
【例题】P1552 [APIO2012]派遣
【题意】
一个点有两个权值,工资和领导力,你有m元预算用来支付工资,请在一个树上任选一个节点作为leader,选择其子树内的若干节点(自己也可以选),保证能支付的起工资,并最大化人员个数*leader的领导力
【分析】
贪心的考虑,选定了一个leader后,一定要尽量选工资便宜的员工,所以这个问题变成了一个求子树最多能去多少个,使得工资和不超过m
所以我们可以在做dfs的时候,向上合并,初始的时候把每个点加成只有自己的堆,然后向上回溯的过程中合并堆,每次访问完子树,合并完堆后,计算自己作leader的ans,弹出自己队列中工资高的,直到总工资不超过m
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
int n,m;
ll c[maxn],l[maxn];
int head[maxn],cnt;
struct edge
{
int to,nxt;
}e[maxn];
void add(int x,int y)
{
e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt;
}
int root,b;
struct tree
{
int ls,rs,fa;
ll val,dis;
}tr[maxn];
ll siz[maxn],valu[maxn];
int merge(int x,int y)
{
if(!x || !y) return x+y;
if(tr[x].val<tr[y].val) swap(x,y);
tr[x].rs=merge(tr[x].rs,y);
if(tr[tr[x].rs].dis>tr[tr[x].ls].dis) swap(tr[x].ls,tr[x].rs);
if(!tr[x].rs || !tr[x].ls) tr[x].dis=0;
else tr[x].dis=tr[tr[x].rs].dis+1;
return x;
}
ll ans;
int del(int x)
{
int l=tr[x].ls,r=tr[x].rs;
tr[l].fa=l; tr[r].fa=r;
tr[x].ls=tr[x].rs=tr[x].dis=0;
return merge(l,r);
}
int id[maxn];
void dfs(int u)
{
siz[u]=1; valu[u]=c[u];
for(int i=head[u];i;i=e[i].nxt)
{
int to=e[i].to;
dfs(to);
id[u]=merge(id[u],id[to]);
siz[u]+=siz[to]; valu[u]+=valu[to];
}
while(valu[u]>m)
{
siz[u]--; valu[u]-=tr[id[u]].val;
id[u]=del(id[u]);
}
ans=max(ans,l[u]*siz[u]);
}
int main()
{
freopen("dispatching.in","r",stdin);
freopen("dispatching.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&b,&c[i],&l[i]);
if(!b) root=i;
else
add(b,i);
id[i]=i;
}
tr[0].dis=-1;
for(int i=1;i<=n;i++) tr[i].fa=i,tr[i].val=c[i];
dfs(root);
printf("%lld\n",ans);
return 0;
}
【练习1】P3261 [JLOI2015]城池攻占
【题意】
【分析】
开始把士兵加到各个节点的队列中,类似例题,做dfs的过程中,合并堆,每次删除hp<0的士兵,并记录death和num的贡献
每个节点的修改可以记录在标记上,注意及时下传即可
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
typedef long long ll;
int n,m;
int head[maxn],cnt,op[maxn],v[maxn],c[maxn];
int s[maxn],dep[maxn],fa[maxn];
ll num[maxn],death[maxn],h[maxn];
struct edge
{
int to,nxt;
}e[maxn];
struct tree
{
int ls,rs,fa;
ll val,dis,mtag,atag;
}tr[maxn];
void add(int x,int y)
{
e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt;
}
int root[maxn];
void pushdown(int x)
{
ll mt=tr[x].mtag,at=tr[x].atag;
if(tr[x].ls)
{
tr[tr[x].ls].mtag*=mt; tr[tr[x].ls].atag*=mt;
tr[tr[x].ls].atag+=at; tr[tr[x].ls].val=tr[tr[x].ls].val*mt+at;
}
if(tr[x].rs)
{
tr[tr[x].rs].mtag*=mt; tr[tr[x].rs].atag*=mt;
tr[tr[x].rs].atag+=at; tr[tr[x].rs].val=tr[tr[x].rs].val*mt+at;
}
tr[x].mtag=1; tr[x].atag=0;
}
int merge(int x,int y)
{
if(!x || !y) return x+y;
pushdown(x),pushdown(y);
if(tr[x].val>tr[y].val) swap(x,y);
tr[x].rs=merge(tr[x].rs,y);
if(tr[tr[x].ls].dis<tr[tr[x].rs].dis) swap(tr[x].ls,tr[x].rs);
tr[x].dis=tr[tr[x].rs].dis+1;
return x;
}
void dfs(int u)
{
dep[u]=dep[fa[u]]+1;
for(int i=head[u];i;i=e[i].nxt)
{
int to=e[i].to;
dfs(to);
root[u]=merge(root[u],root[to]);
}
while(tr[root[u]].val<h[u] && root[u])
{
pushdown(root[u]);
death[u]++;
num[root[u]]=dep[c[root[u]]]-dep[u];
root[u]=merge(tr[root[u]].ls,tr[root[u]].rs);
}
if(!op[u])
{
tr[root[u]].atag+=v[u];
tr[root[u]].val+=v[u];
}
else
{
tr[root[u]].mtag*=v[u];
tr[root[u]].atag*=v[u];
tr[root[u]].val*=v[u];
}
}
int main()
{
freopen("city.in","r",stdin);
freopen("city.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&h[i]);
int x;
for(int i=2;i<=n;i++)
{
scanf("%d%d%lld",&fa[i],&op[i],&v[i]);
add(fa[i],i);
}
for(int i=1;i<=m;i++)
{
scanf("%lld%d",&tr[i].val,&c[i]);
root[c[i]]=merge(root[c[i]],i);
tr[i].mtag=1;
}
dfs(1);
while(root[1])
{
pushdown(root[1]);
num[root[1]]=dep[c[root[1]]];
root[1]=merge(tr[root[1]].ls,tr[root[1]].rs);
}
for(int i=1;i<=n;i++) printf("%lld\n",death[i]);
for(int i=1;i<=m;i++) printf("%lld\n",num[i]);
return 0;
}