樹鏈剖分詳解

2022-04-30 13:24:13 字數 4044 閱讀 5342

重兒子:對於每乙個非葉子節點,它的兒子中 以那個兒子為根的子樹節點數最大的兒子 為該節點的重兒子 (ps: 感謝@shzr大佬指出我此句話的表達不嚴謹qwq, 已修改)

輕兒子:對於每乙個非葉子節點,它的兒子中 非重兒子 的剩下所有兒子即為輕兒子

葉子節點沒有重兒子也沒有輕兒子(因為它沒有兒子。。)

重邊:乙個父親連線他的重兒子的邊稱為重邊 //原寫法:連線任意兩個重兒子的邊叫做重邊

輕邊:剩下的即為輕邊

重鏈:相鄰重邊連起來的 連線一條重兒子 的鏈叫重鏈

對於葉子節點,若其為輕兒子,則有一條以自己為起點的長度為1的鏈

每一條重鏈以輕兒子為起點

這圖好像是洛咕上的,我還是懶得自己畫

說起來這些概念實際很簡單

但寫起來還是要有較強碼力的

我們先要寫把輕重鏈求出的函式

一共需要寫兩個函式

1.dfs1

dfs1主要求出:

1.該節點的子樹大小(1+所有子節點子樹大小之和)

2.重兒子(找到所有子節點中子樹大小最大的)

3.父節點

4.深度

dfs1還是比較簡單的qaq

inline void dfs1(register int x)

}

dfs2dfs2是重鏈剖分的重點

dfs2要求出:

1.樹的dfs序(優先搜重兒子)

2.在樹的dfs序之下,珂以把樹上的值存到連續的數列中,到時就珂以線段樹維護

3.每個重鏈的頂端,方便到時候跳鏈(不懂的話後面會講)

inline void dfs2(register int x,register int t)

跑完兩個dfs之後就珂以用線段樹

build建樹:

inline void pushup(register int x)

inline void build(register int x,register int l,register int r)

int mid=l+r>>1;

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

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

pushup(x);

}

下面是處理查詢

操作1:把x節點到y節點路徑上的值加z

這裡需要乙個跳鏈的函式——cal1

inline void pushdown(register int x,register int l,register int r)

inline void update(register int x,register int l,register int r,register int l,register int r,register int k)

if(tag[x])

pushdown(x,l,r);

int mid=l+r>>1;

if(l<=mid)

update(x<<1,l,mid,l,r,k);

if(r>=mid+1)

update(x<<1|1,mid+1,r,l,r,k);

pushup(x);

}inline void cal1(register int x,register int y,register int z)

操作2:查詢x到y路徑點權之和

和操作1差不多,需要跳鏈

inline ll query(register int x,register int l,register int r,register int l,register int r)

inline ll cal2(register int x,register int y)

操作3:把x的子樹內所有節點全值加z

考慮到子樹內dfs序是相連的

所以被修改區間是乙個連續的區間,所以直接上線段樹

update(1,1,tot,dl[x],dl[x]+size[x]-1,z%mod);
操作四:求x的子樹內所有節點的和

和操作3一樣,珂以直接用線段樹

write(query(1,1,tot,dl[x],dl[x]+size[x]-1)%mod);
#include #define ll long long

#define n 100005

using namespace std;

inline ll read()

while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();

return x*f;

}inline void write(register ll x)

struct nodee[n<<1];

int head[n],cnt=0;

inline void add(register int u,register int v)

; head[u]=cnt;

}ll ch[n];

ll n,m,rt,mod;

ll size[n],dep[n],fa[n],son[n];

ll tot=0,dl[n],a[n],top[n];

inline void dfs1(register int x)

}inline void dfs2(register int x,register int t)

ll sum[n<<3],tag[n<<3];

inline void pushup(register int x)

inline void build(register int x,register int l,register int r)

int mid=l+r>>1;

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

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

pushup(x);

}inline void pushdown(register int x,register int l,register int r)

inline void update(register int x,register int l,register int r,register int l,register int r,register int k)

if(tag[x])

pushdown(x,l,r);

int mid=l+r>>1;

if(l<=mid)

update(x<<1,l,mid,l,r,k);

if(r>=mid+1)

update(x<<1|1,mid+1,r,l,r,k);

pushup(x);

}inline ll query(register int x,register int l,register int r,register int l,register int r)

inline void cal1(register int x,register int y,register int z)

inline ll cal2(register int x,register int y)

int main()

{ n=read(),m=read(),rt=read(),mod=read();

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

ch[i]=read(),ch[i]%=mod;

for(register int i=1;i1.luogu p2146 [noi2015]軟體包管理器

樹剖練手好題

2.luogu cf343d water tree

樹剖後用珂朵莉樹

3.luogu cf375d tree and queries

樹剖後莫隊暴力求解(也可以稱之為樹的dfs序)

4.luogu p4069 [sdoi2016]遊戲

樹剖+李超線段樹

樹鏈剖分詳解

樹鏈剖分定義 只是把一棵樹拆成鏈來處理而已,即將樹上的某些段一起通過資料結構優化進行處理來降低複雜度。樹鏈剖分相關定義 重兒子 ve v 為 u 的子節點中ve 值最大的,那麼 v 就是 u的重兒子 將子樹中最長的那一條鏈一起處理來降低複雜度 輕兒子 u 除了重兒子的其它子節點。重邊 點 u與其重兒...

詳解樹鏈剖分

樹鏈剖分,顧名思義為將鏈剖開分成多條。當我們想要修改樹上一條路的值或求值時,我們暴力只能用乙個個修改,這是非常慢的。這時,我們就要想乙個辦法,資料結構?但是資料結構我們都需要連續修改,可是樹上路徑的編號是不連續的。於是我們想了乙個辦法。我們先定義 fa x 為x的父親 dep x 為x的深度 siz...

樹鏈剖分詳解

樹鏈剖分,說白了就是一種讓你 不得不強行增加1k的資料結構 dms 個人理解 1 joy 證明出題人非常毒瘤 可以非常友 bao 好 li 的解決一些樹上問題 grimacing 樹鏈剖分的思想比較神奇 它的思想是 把一棵樹拆成若干個不相交的鏈,然後用一些資料結構去維護這些鏈 那麼問題來了 首先明確...