動態樹之(霧)樹鏈剖分

2022-05-12 02:33:41 字數 3349 閱讀 2509

這貨是不是動態樹里的我就不清楚了,fhq的blog好像有提到orz

一些不需要link-cut操作的樹上路徑的題可以用樹鏈剖分做,常數比lct小多了。//upd:所以這已經不是動態樹了囧。。。。。標題我就不改了。。。。。。還好原來機智打了個「霧」

學習了下hld(樹鏈剖分),嗯,挺簡單的。hld可以在樹中的操作有很多,hld可以說只是一種概念結構,它可以套很多其它的資料結構來進行操作,比如我現在只要求路徑最值和求和,那麼套線段樹就行了;如果我要求第k大,可以套splay和主席樹(這個不知道),也可以套分塊(不會,分塊以後學,必須學。。)但是我覺得,樹剖比lct還要難寫。。我lct一下就能寫出來了。。可是lct的常數,不忍直視。。概念:

重兒子:num[u]為v的子節點中num值最大的,那麼u就是v的重兒子。

輕兒子:v的其它子節點。

重邊:點v與其重兒子的連邊。

輕邊:點v與其輕兒子的連邊。

重鏈:由重邊連成的路徑。

輕鏈:輕邊。

剖分後的樹有如下性質:

性質1:如果(v,u)為輕邊,則siz[u] * 2 < siz[v];

性質2:從根到某一點的路徑上輕鏈、重鏈的個數都不大於logn。

我們來說他怎麼在路徑操作吧:

下邊是乙個例圖:黑邊為重邊,帶紅點的為重邊組成的鏈的最頂點(沒有重邊的點最頂點就是它自己),藍字為重邊的序號

哈哈,發現了什麼嗎?

重鏈的序號是連續的。 不會有兩條重鏈相交~~

哈哈?套各種樹維護啦~~~。

我們在這個連續的區間操作就行了,然後不斷向根走,直到走到最頂點相交(這裡不是重鏈相交,是最頂點相交,即一條輕邊和重邊的交點)

向上走的時候,不是乙個個走,那麼效率大大提高啦~。。這就是樹鏈剖分的主體思想。

我們在這些鏈(或點)重新標號後,用各種資料結構維護資訊。

求樹剖需要維護的域很簡單,兩個深搜搞定,大家自己想吧,我不說了(要是想不通,,點這:

例題:基於點分類:【bzoj】1036: [zjoi2008]樹的統計count(lct/樹鏈剖分)

#include #include using namespace std;

#define dbg(x) cout << #x << "=" << x << endl

#define read(x) x=getint()

#define print(x) printf("%d", x)

#define lc x<<1

#define rc x<<1|1

#define lson l, m, lc

#define rson m+1, r, rc

#define mid (l+r)>>1

const int oo=~0u>>1;

inline int getint()

const int n=30010, m=100005;

int ihead[n], inext[m], to[m], cnt, n, m;

int top[n], son[n], fa[n], dep[n], sz[n], id[n], a[n], b[n], tot;

int l, r, key;

struct node t[n*50];

inline const int max(const int& a, const int& b)

inline void pushup(const int &x)

void dfs1(const int &u)

}void dfs2(const int &u, const int &tp)

void build(const int &l, const int &r, const int &x)

int m=mid;

build(lson); build(rson);

pushup(x);

}void update(const int &l, const int &r, const int &x)

int m=mid;

if(l<=m) update(lson);

if(mdep[y]) swap(x, y);

l=id[x], r=id[y];

return max(ret, getmax(1, n, 1));

}inline int query(int x, int y)

inline void add(const int &u, const int &v)

int main()

inline int getint()

const int n=50010, oo=~0u>>1;

struct ed e[n];

int ihead[n], inext[n<<1], to[n<<1], cnt;

int fa[n], sz[n], son[n], top[n], dep[n], id[n], mx[n*5], num[n], tot, l, r, key, n;

inline void pushup(const int &x)

void build(const int &l, const int &r, const int &x)

int m=mid;

build(lson); build(rson);

pushup(x);

}void update(const int &l, const int &r, const int &x)

int m=mid;

if(l<=m) update(lson); if(msz[son[u]]) son[u]=v; }}

void dfs2(const int &u, const int &tp)

inline int getmax(int x, int y)

inline void add(const int &u, const int &v)

int main()

build(2, n, 1);

for(ch=getchar(); ch'z'; ch=getchar());

while(ch!='d')

else printf("%d\n", getmax(a, b));

for(ch=getchar(); ch'z'; ch=getchar());

} }return 0;

}

樹鏈剖分 樹鏈剖分講解

好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...

樹鏈剖分 BZOJ3589 動態樹

time limit 30 sec memory limit 1024 mb submit 543 solved 193 submit status discuss 別忘了這是一棵動態樹,每時每刻都是動態的.小明要求你在這棵樹上維護兩種事件 事件0 這棵樹長出了一些果子,即某個子樹中的每個節點都會長...

樹鏈剖分 樹剖換根

這是一道模板題。給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有乙個給定的權值。下面依次進行 m 個操作,操作分為如下五種型別 換根 將乙個指定的節點設定為樹的新根。修改路徑權值 給定兩個節點,將這兩個節點間路徑上的所有節點權值 含這兩個節點 增加乙個給定的值。修改子樹權值 給定乙...