1. 程式人生 > >關於樹論【左偏樹】

關於樹論【左偏樹】

pri space blog 表示 eap clas amp 記得 ont

還記得當年坐在OZY大佬旁邊被D的日子。。才發現現在妙已經變成權限題做不了(怕是要被DS)只能補補左偏樹聊以自慰了。

這個東西呢其實也是堆的一種(也叫左偏堆),可以理解為維護大(小)根堆的,堆頂就是最大(小)值用d表示,然後l,r是左右孩子節點,c是管理人數。至於為什麽叫做左偏樹呢,是因為他一個奇怪的定義:左邊的子樹的節點數一定大於右邊的子樹的節點數,這樣有什麽好處呢?顯然,我們如果插入節點從左往右,那就可以保證樹的平衡。

其實這樣看來,這個東西挺普通的,只是方便求最大(小)值,或者再拓展一下,管理多個集合,但是,它有一個關鍵的操作,就是Merge——合並!他可以支持兩個左偏樹的合並,具體怎麽做呢?跟插入一個點是一樣的,在右邊插入,然後通過交換左右孩子,繼續保持左偏性質,所以這個有一個限制——左右孩子可以交換,就是說不像伸展樹類似的,左大右小什麽的。

bzoj1455(現在也變成權限題了)時光荏再,物是人非啊。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
struct heap
{
    int l,r,c,d;
    heap()
    {
        l=0;r=0;c=0;
    }
}h[1100000];
int Merge(int x,int y)
{
    if(x==0||y==0)return x+y;
    if(h[x].d>h[y].d)swap(x,y);//
維護小根堆 h[x].r=Merge(h[x].r,y);//讓他到右邊合並,令樹平衡 if(h[h[x].l].c<h[h[x].r].c)swap(h[x].l,h[x].r);//保持左偏 h[x].c=h[h[x].r].c+1;//維護 return x; } int fa[1100000]; int findfa(int x)//用並查集來維護每一個團 { if(fa[x]==x)return x; fa[x]=findfa(fa[x]);return fa[x]; } bool v[1100000];//人有沒有被殺 char
ss[5]; int main() { int n,m,x,y; scanf("%d",&n); for(int i=1;i<=n;i++) { fa[i]=i; scanf("%d",&h[i].d); } memset(v,false,sizeof(v)); scanf("%d",&m); while(m--) { scanf("%s",ss+1); if(ss[1]==M) { scanf("%d%d",&x,&y); if(v[x]==true||v[y]==true)continue; int fx=findfa(x),fy=findfa(y); if(fx==fy)continue;//是否在一個團裏面 fa[fx]=fa[fy]=Merge(fx,fy);//合並兩個堆,維護並查集,祖先是值最小的人 } else { scanf("%d",&x); if(v[x]==true){printf("0\n");continue;} int k=findfa(x);v[k]=true;//殺人 printf("%d\n",h[k].d); fa[k]=Merge(h[k].l,h[k].r);//祖先是新的值最小的人 fa[fa[k]]=fa[k]; } } return 0; }

關於樹論【左偏樹】