1. 程式人生 > >可並堆

可並堆

線段樹合並 define http har nod namespace 復雜度 scan The

可並堆

可並堆,又稱為左偏樹,滿足從一個節點一直向左兒子走比一直向右兒子走距離更長。

這樣,它就滿足了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

可並堆