可并堆
可并堆,又称为左偏树,满足从一个节点一直向左儿子走比一直向右儿子走距离更长。
这样,它就满足了往右走最多log次,也就是每次合并的时间复杂度为O(log)
合并:将一个合并到另一个的右儿子上,合并的同时满足堆的所有性质。
BZOJ1455罗马游戏:
维护小根堆,每次合并的时候讲大的合并到小的的右儿子上。
可并堆例题,没有什么多说的,权值线段树合并应该也可以做。
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
#include <cstdlib>
using namespace std;
#define N 1000005
int fa[N],n,Q;
char s[2];
struct node
{
int ls,rs,x,dis;
}mp[N];
int merge(int x,int y)
{
if(!x)return y;
if(!y)return x;
if(mp[x].x>mp[y].x)swap(x,y);
mp[x].rs=merge(mp[x].rs,y);
if(mp[mp[x].ls].dis<mp[mp[x].rs].dis)swap(mp[x].ls,mp[x].rs);
mp[x].dis=mp[mp[x].rs].dis+1;
return x;
}
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
bool vis[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
fa[i]=i;
scanf("%d",&mp[i].x);
}
scanf("%d",&Q);
while(Q--)
{
int x,y;
scanf("%s%d",s,&x);
if(s[0]=='M')
{
scanf("%d",&y);
if(vis[x]||vis[y])continue;
int fx=find(x),fy=find(y);
if(fx==fy)continue;
fa[fx]=fa[fy]=merge(fx,fy);
}else
{
if(vis[x])
{
printf("0\n");
continue;
}
int fx=find(x);
printf("%d\n",mp[fx].x);
fa[fx]=fa[mp[fx].ls]=fa[mp[fx].rs]=merge(mp[fx].ls,mp[fx].rs);
mp[fx].rs=mp[fx].ls=0;
vis[fx]=1;
}
}
return 0;
}
BZOJ2809: [Apio2012]dispatching
我们考虑,维护大根堆,费用大于M就弹出堆顶,在给出的树上完成操作,用dfs实现。
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
#define N 100005
#define ll long long
struct no
{
int to,next;
}e[N];
int head[N],cnt,a[N],b[N],fa[N],n,m;
struct node
{
int ls,rs,x,dis;
}mp[N];
void add(int x,int y)
{
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt++;
return ;
}
int find(int x)
{
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
}
int merge(int x,int y)
{
if(!x)return y;
if(!y)return x;
if(mp[x].x<mp[y].x)swap(x,y);
mp[x].rs=merge(mp[x].rs,y);
if(mp[mp[x].ls].dis<mp[mp[x].rs].dis)swap(mp[x].ls,mp[x].rs);
mp[x].dis=mp[mp[x].rs].dis+1;
return x;
}
ll ans,sum[N];
int siz[N];
void dfs(int x,int from)
{
sum[x]=a[x];
siz[x]=1;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from)
{
dfs(to1,x);
sum[x]+=sum[to1];
int fx=find(x),fy=find(to1);
fa[fx]=fa[fy]=merge(fx,fy);
siz[x]+=siz[to1];
}
}
while(sum[x]>m)
{
siz[x]--;
int fx=find(x);
sum[x]-=mp[fx].x;
mp[fx].x=0;
fa[fx]=fa[mp[fx].ls]=fa[mp[fx].rs]=merge(mp[fx].ls,mp[fx].rs);
}
ans=max(1ll*b[x]*siz[x],ans);
return ;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
fa[i]=i;
int x;
scanf("%d%d%d",&x,&a[i],&b[i]);
mp[i].x=a[i];
if(!x)continue;
add(x,i);
}
dfs(1,0);
printf("%lld\n",ans);
return 0;
}
BZOJ3011: [Usaco2012 Dec]Running Away From the Barn
这题方法不少,可并堆可以实现,每次将距离大于L的弹出堆顶,同时需要记录堆中元素个数,比较水,记得开long long
附上代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
#include <cstdlib>
using namespace std;
#define N 200005
#define ll long long
struct node
{
int dis,ls,rs;
ll x;
}mp[N];
struct no
{
int to,next;
ll val;
}e[N];
int head[N],cnt;
void add(int x,int y,ll z)
{
e[cnt].to=y;
e[cnt].val=z;
e[cnt].next=head[x];
head[x]=cnt++;
return ;
}
int fa[N],n,siz[N];
ll dep[N],L;
int merge(int x,int y)
{
if(!x)return y;
if(!y)return x;
if(mp[x].x<mp[y].x)swap(x,y);
mp[x].rs=merge(mp[x].rs,y);
if(mp[mp[x].rs].dis>mp[mp[x].ls].dis)swap(mp[x].ls,mp[x].rs);
mp[x].dis=mp[mp[x].rs].dis+1;
return x;
}
int find(int x)
{
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
}
void dfs(int x,int from)
{
siz[x]=1;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from)
{
mp[to1].x=dep[to1]=dep[x]+e[i].val;
dfs(to1,x);
siz[x]+=siz[to1];
int fx=find(x),fy=find(to1);
fa[fx]=fa[fy]=merge(fx,fy);
}
}
int fx=find(x);
while(mp[fx].x>L+dep[x])
{
fa[fx]=fa[mp[fx].ls]=fa[mp[fx].rs]=merge(mp[fx].ls,mp[fx].rs);
siz[x]--;
fx=find(x);
}
return ;
}
int main()
{
fa[1]=1;
memset(head,-1,sizeof(head));
scanf("%d%lld",&n,&L);
for(int i=2;i<=n;i++)
{
fa[i]=i;
int x;
ll y;
scanf("%d%lld",&x,&y);
add(x,i,y);
}
dfs(1,0);
for(int i=1;i<=n;i++)
{
printf("%d\n",siz[i]);
}
return 0;
}
BZOJ4003: [JLOI2015]城池攻占
我写过这题的题解,就不重复了,附上链接:https://www.cnblogs.com/Winniechen/p/8890801.html
BZOJ3252: 攻略
网上的题解给的都是线段树+dfs序,挺裸的,可并堆也可以实现,跑的飞起,代码精悍。附上链接:https://www.cnblogs.com/Winniechen/p/8990559.html