重兒子:乙個有根樹的乙個點 子樹最大的兒子
輕兒子:其它的兒子
重鏈:由重兒子連線成的鏈
輕鏈:其它的所有鏈
下圖是一棵剖好的樹
來自於[知識點]樹鏈剖分
樹剖本體其實只有兩個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)
}
接下來就可以帶入資料結構解決問題
考慮線段樹
update
及query
為普通線段樹的處理
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 來記錄它,父親節點到...