可并堆

可并堆

可并堆,又称为左偏树,满足从一个节点一直向左儿子走比一直向右儿子走距离更长。

这样,它就满足了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

猜你喜欢

转载自www.cnblogs.com/Winniechen/p/9095448.html