樹鏈剖分學習筆記

2022-06-02 03:12:07 字數 3995 閱讀 9084

重兒子:乙個有根樹的乙個點 子樹最大的兒子

輕兒子:其它的兒子

重鏈:由重兒子連線成的鏈

輕鏈:其它的所有鏈

下圖是一棵剖好的樹

來自於[知識點]樹鏈剖分

樹剖本體其實只有兩個dfs

第乙個dfs處理每個子樹的大小,重兒子一類的資訊

第二個dfs處理剖出的鏈的資訊

void dfs1(int x) 

}}

void dfs2(int x, int tp) 

for(int i = head[x]; i; i = e[i].next)

}

我們手上現在有剖好的鏈

我們一次上跳就可以跳一條鏈的長度,所以時間複雜度大大降低

int getlca(int x, int y) 

y = fa[f2];//將y向上跳f2的父節點即是別的鏈的一部分

f2 = top[y];//更新鏈頂

} if(dep[x] < dep[y])

else return y;

}

例題

描述已知一棵包含n個結點的樹(連通且無環),每個節點上包含乙個數值,需要支援以下操作:

操作1: 格式: 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z

操作2: 格式: 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和

操作3: 格式: 3 x z 表示將以x為根節點的子樹內所有節點值都加上z

操作4: 格式: 4 x 表示求以x為根節點的子樹內所有節點值之和

既然想要處理鏈上資訊

我們就想要處理的資訊連續

dfs序幫我們解決了這個問題

按dfs遍歷到的順序儲存下節點即可

void dfs2(int x, int tp) 

for(int i = head[x]; i; i = e[i].next)

}

接下來就可以帶入資料結構解決問題

考慮線段樹

updatequery為普通線段樹的處理

void update_lst(int x, int y, int z) 

update(1, 1, n, dfn[top[x]], dfn[x], z);//不斷對較低的點所在的鏈處理,

x = fa[top[x]];

} if(dep[x] > dep[y]) swap(x, y);

update(1, 1, n, dfn[x], dfn[y], z);//當兩點在一條鏈上時

}

update_lst()差不多

int query_lst(int x, int y) 

ret = (ret + query(1, 1, n, dfn[top[x]], dfn[x])) % p;

x = fa[top[x]];

} if(dep[x] > dep[y]) swap(x, y);

ret = (ret + query(1, 1, n, dfn[x], dfn[y])) % p;

return ret;

}

因為dfs序的處理現在每條鏈的下標都是連續的,長度就是siz[x]

void update_tre(int x, int z)
int query_tre(int x)
#include #include #include #include #include #include #include #define maxn 100007

using namespace std;

int n, m, r, p, tot;

int val[maxn];

int head[maxn], cnt;

struct node e[maxn << 1];

void add(int u, int v)

int son[maxn], fa[maxn], top[maxn], siz[maxn];

int dfn[maxn], dep[maxn], wt[maxn];

void dfs1(int x) }}

void dfs2(int x, int tp)

for(int i = head[x]; i; i = e[i].next)

}int tree[maxn << 3], lazy[maxn << 3];

void pushup(int o)

void pushdown(int o, int l, int r)

void build(int o, int l, int r)

int mid = (l + r) >> 1;

build(o << 1, l, mid);

build(o << 1 | 1, mid + 1, r);

pushup(o);

}void update(int o, int l, int r, int ql, int qr, int val)

pushdown(o, l, r);

int mid = (l + r) >> 1;

if(ql <= mid) update(o << 1, l, mid, ql, qr, val);

if(qr > mid) update(o << 1| 1, mid + 1, r, ql, qr, val);

pushup(o);

}int query(int o, int l, int r, int ql, int qr)

pushdown(o, l, r);

int mid = (l + r) >> 1, ret = 0;

if(ql <= mid) ret = (ret + query(o << 1, l, mid, ql, qr)) % p;

if(qr > mid) ret = (ret + query(o << 1 | 1, mid + 1, r, ql, qr)) % p;

return ret % p;

}void update_lst(int x, int y, int z)

update(1, 1, n, dfn[top[x]], dfn[x], z);

x = fa[top[x]];

} if(dep[x] > dep[y]) swap(x, y);

update(1, 1, n, dfn[x], dfn[y], z);

}int query_lst(int x, int y)

ret = (ret + query(1, 1, n, dfn[top[x]], dfn[x])) % p;

x = fa[top[x]];

} if(dep[x] > dep[y]) swap(x, y);

ret = (ret + query(1, 1, n, dfn[x], dfn[y])) % p;

return ret;

}void update_tre(int x, int z)

int query_tre(int x)

int main()

for(int i = 1; i < n; i++)

dfs1(r);

dfs2(r, r);

build(1, 1, n);

for(int i = 1; i <= m; i++)

if(op == 2)

if(op == 3)

if(op == 4)

}}

樹鏈剖分學習筆記

寫 又犯了很sb的錯誤,線段樹寫錯了。好像每次都會把r l 1寫成l r 1,然後就只有20分。寫的比較醜,壓了壓之後190行。基本上是我打過的最長的乙個模板了 然後簡單介紹一下樹剖吧。樹鏈剖分,就是把樹剖分成鏈,然後用資料結構來維護這些鏈,使得詢問 修改的複雜度達到o logn o l ogn 不...

樹鏈剖分學習筆記

樹鏈剖分 mod estc oder modestcoder modest code r如果你是重兒子,你就在重路徑上。如果你是輕兒子,暴力沿著祖先向上爬最多log nlogn logn 次就可以遇到重路徑。或者到根 而樹上操作基本就是找祖先 也許有人喜歡我的碼風 include include d...

樹鏈剖分學習筆記

前言 書上只講了重鏈剖分,菜雞也只會這一種,想看其他的是別想了。要會樹鏈剖分,首先你需要了解一些概念。我們把乙個節點的所有兒子節點中子樹節點數最大的稱為重兒子,也就是size最大的子節點。size的定義我在講換根dp時說過,因此不再贅述。對於每個節點的重兒子,我們用 son x 來記錄它,父親節點到...