學習筆記 倍增 樹鏈剖分

2021-09-24 18:15:43 字數 2587 閱讀 1396

給你一棵樹,包括它的邊和根節點,詢問2個節點的最近公共祖先

容易想到一種方法,先dfs一遍,然後對於詢問的2個節點,先跳到同一高度,然後一起往上跳

好像是logn的演算法呢,emmm卡成一條鏈怎麼辦,這就退化成nm了

倍增是什麼東東捏,就是在往上跳的過程中不是一次跳一格,而是跳2^k次方

就算是一條鏈,也可以在近似logn的時間內求解,那到底怎麼做呢

先dfs一遍,需要求出某乙個節點的祖先們(即它往上2^0深度的,往上2^1深度的等等),求出節點的深度

有人會問,祖先們怎麼求啊,乙個個搜上去嗎

這是不可能滴,有一種簡單的方法,乙個點的2^i代祖先就是它的2^i-1代祖先的2^i-1代祖先,即2^i=2^i-1 + 2^i-1

那之後怎麼處理呢,當x點與y點深度不同時,將深度大的點往上跳,直到深度一樣

然後當該兩個點的第2^i代祖先不同時,就將該兩個點同時跳到第2^i代祖先,然後繼續,這裡i是從大到小搜,所以一定能搜到

之後返回搜到的值就可以了,還是挺快的

#includeusing namespace std;

struct pa[1000010];

struct pointpo[500010];

int n,m,s,head[500010],x,y,l=0,lg[500010];

void add(int i,int j)

void dfs(int u,int f)

int main()

}

歷經了千辛萬苦好像不苦,我們把準備工作做完了emmmm?

這棵樹已經切好了,接下來build一下,不用上**了吧qwq,樹就建完了

可是來回顧一下我們需要支援的操作:詢問或修改從x點到y點的路徑,詢問或修改以x為根節點的子樹所有的節點

還是看不出來什麼呀,不要急qwq,慢慢看下去

對於詢問和修改,只要找到了需要詢問和修改的區間,其他的操作和線段樹是一樣的,所以我們主要來講兩種詢問

第一種,處理任意兩點間的距離,我們把這兩個點所在的鏈的鏈頂拉出來,比較一下它們的深度,設x為鏈頂深度大的那個節點

ans加上x到所在鏈頂的這一段距離,然後將x跳到所在鏈頂的父親節點

重複這2個步驟,直到2個節點所在鏈頂都一樣,ans加上這兩個點的區間和即可

有人說不知道一條鏈上的距離怎麼求,我們之前不是建過樹了嗎emm,一條鏈一定是連續的乙個區間,上個**吧qwq

long long aska(long long x,long long y)

第二種,處理乙個點及其子樹的點權,還記得我們之前求過的size陣列嗎,因為是按照dfs序存的線段樹,所以啊,很簡單嘛qwq

直接上一下**

long long askb(long long x)

//就是x的id往後推size[x]個,這一段就是x及其子樹的和

多說一句啊,樹鏈剖分還可以應用於引入1中的問題

???怎麼用啊你是傻子嗎

我們剖完這一棵樹後,對於需要查詢最近公共祖先的兩個點,如果兩個點在一條鏈上,返回深度小的點

如果不在一條鏈上,就將兩個節點所在鏈的鏈頂節點中深度大的點跳到它的父親節點,重複這個操作直到兩個點在一條鏈上

是不是很簡單qwq

**好像比倍增長很多這不是重點啊喂

上一下經過我的神仙壓行後還是很長的**,這份**裡支援了引入1和引入2的各種功能

#includeusing namespace std;

struct edgee[200010];

struct pa[200010];

long long n,m,r,p,opt,x,y,z,len=1,f[100010],dep[100010],size[100010],son[100010],top[100010],id[100010];

long long b[100010],head[100010],cnt=0,c[100010];

void pushup(long long nw)

void pushdown(long long l,long long r,long long nw)

void build(long long l,long long r,long long nw)

}void add(long long l,long long r,long long nw,long long ll,long long rr,long long k)

}void dfsb(long long v)

}long long lca(long long x,long long y)

if(dep[x]>dep[y])return y;

else return x;

}long long aska(long long x,long long y)

long long askb(long long x)

long long adda(long long x,long long y,long long k)

long long addb(long long x,long long k)

int main()

return 0;

}

樹鏈剖分學習筆記

寫 又犯了很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 來記錄它,父親節點到...