樹鏈剖分,顧名思義為將鏈剖開分成多條。
當我們想要修改樹上一條路的值或求值時,我們暴力只能用乙個個修改,這是非常慢的。這時,我們就要想乙個辦法,資料結構?但是資料結構我們都需要連續修改,可是樹上路徑的編號是不連續的。於是我們想了乙個辦法。
我們先定義
fa[x]為x的父親
dep[x]為x的深度
size[x]為以x為根的子樹的節點個數
幾個名詞:
1.重邊(乙個節點的兒子中size最大的點與這個點之間的邊)
2.重兒子 x的重兒子記為son[x](乙個節點重邊連線的兒子)
3.輕邊(乙個節點除重兒子外與其他兒子鏈結的邊)
4.輕兒子 (x除重兒子外的兒子)
5.重鏈(由重邊連線而成的鏈)
6.輕鏈(由輕邊連線而成的鏈)
然後我們再定義
top[x]為x所在的重鏈中深度最小的節點(若x不為fa[x]的重兒子則top[x]=x,即自己乙個點形成一條重鏈)
我們舉個例子,在這棵樹中,son[1]=2(因為size[2]比size[3]和size[4]大)。
1,2,6,9,11構成一條重鏈;3,7,10構成一條重鏈。
top[1]=top[2]=top[6]=top[9]=top[11]=1
top[3]=top[7]=top[10]=3
top[5]=5 top[4]=4 top[8]=8
然後又這樣一條性質,從根到任意一節點的路徑上輕鏈,重鏈的個數都不大於logn(n為節點個數)。
現在我們將一條重鏈上連續標號,建立新的標號(這是為了可以在資料結構中連續修改)。
我們dfs遍歷,優先走重兒子,則上面那個圖的新標號tid為:
tid[1]=1 tid[2]=2 tid[6]=3 tid[9]=4 tid[11]=5
tid[5]=6
tid[3]=7 tid[7]=8 tid[10]=9
tid[8]=10
tid[4]=11
我們舉個例子:
上圖中從10~11,即u=10,v=11,因為top[v]>top[u],那麼我們先使u跳到fa[top[10]]=1,然後u和v就在一條重鏈上了,那麼我們就可以直接修改了。
bzoj1036[zjoi2008]樹的統計count
這題是樹鏈剖分裸題,下面給出此題**以供參考和理解。
#include
#include
#include
#include
#include
#define n 30500
using namespace std;
int n,tot,lable,next[n*2],head[n*2],tree[n*2],a[n],m,fa[n],dep[n],son[n],size[n],tid[n],number[n];
int top[n],max[n*8],sum[n*8];
bool visit[n];
void add(int x,int y)
void dfs(int x,int depth,int father)
}}void dfs1(int x,int ancestor)
void up(int x)
void build(int l,int r,int
id) int mid=(l+r)/2;
build(l,mid,id*2);
build(mid+1,r,id*2+1);
up(id);
}void change(int x,int l,int r,int
id,int d)
int mid=(l+r)/2;
change(x,l,mid,id*2,d);
change(x,mid+1,r,id*2+1,d);
up(id);
}int query(int x,int y,int
id,int l,int r,int q)
if (x<=l&&r<=y)
int mid=(l+r)/2;
if (q==0) return max(query(x,y,id*2,l,mid,q),query(x,y,id*2+1,mid+1,r,q));
else
return query(x,y,id*2,l,mid,q)+query(x,y,id*2+1,mid+1,r,q);
up(id);
}int query(int x,int y,int q)
if (dep[x]>dep[y]) swap(x,y);
if (q==0) ans=max(ans,query(tid[x],tid[y],1,1,n,0));
else ans+=query(tid[x],tid[y],1,1,n,1);
return ans;
}int main()
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
for (int i=1;i<=n;i++) visit[i]=false;
dfs(1,1,0);
for (int i=1;i<=n;i++) visit[i]=false;
dfs1(1,1);
build(1,n,1);
char s[100];
for (int i=1;i<=m;i++)
return
0;}
以下鏈結是一些關於樹鏈剖分題的部落格鏈結
鏈結
樹鏈剖分詳解
樹鏈剖分定義 只是把一棵樹拆成鏈來處理而已,即將樹上的某些段一起通過資料結構優化進行處理來降低複雜度。樹鏈剖分相關定義 重兒子 ve v 為 u 的子節點中ve 值最大的,那麼 v 就是 u的重兒子 將子樹中最長的那一條鏈一起處理來降低複雜度 輕兒子 u 除了重兒子的其它子節點。重邊 點 u與其重兒...
樹鏈剖分詳解
重兒子 對於每乙個非葉子節點,它的兒子中 以那個兒子為根的子樹節點數最大的兒子 為該節點的重兒子 ps 感謝 shzr大佬指出我此句話的表達不嚴謹qwq,已修改 輕兒子 對於每乙個非葉子節點,它的兒子中 非重兒子 的剩下所有兒子即為輕兒子 葉子節點沒有重兒子也沒有輕兒子 因為它沒有兒子。重邊 乙個父...
樹鏈剖分詳解
樹鏈剖分,說白了就是一種讓你 不得不強行增加1k的資料結構 dms 個人理解 1 joy 證明出題人非常毒瘤 可以非常友 bao 好 li 的解決一些樹上問題 grimacing 樹鏈剖分的思想比較神奇 它的思想是 把一棵樹拆成若干個不相交的鏈,然後用一些資料結構去維護這些鏈 那麼問題來了 首先明確...