詳解:
樹鏈剖分的本質是序
以盡量走重鏈為序,使一棵樹的結點盡量集中地分解成較少的鏈,而鏈作為連續的資料結構是易於維護的
剖分的過程有兩遍dfs :
dfs1:找出重結點 ,確立各個點的深度 ,這個過程可以確定:sz[i] ,dep[i] ,fa[i] ,son[i]
void dfs1( int u ,intf )}
void dfs2( int u ,intt )}
剖分後就可以利用線段樹,樹狀陣列等資料結構對分解後的鏈進行維護,給定兩個節點x,y,進行它們之間的更新和查詢操作,這個過程是:
1.如果x,y在一條重鏈上( top[x]==top[y] ),則可以直接利用資料結構對x到y這一段連續的鏈進行直接操作
2.如果x,y不在一條重鏈上( top[x] != top[y] ),找出較深的結點(dep[fx]>dep[fy])x,先將x到fx這一小段進行操作,再令x向其重鏈之上的父節點轉移
重複這一過程,直到完成x到y之間完整路徑的操作
這一過程也是尋找x,y lca 的過程
int lca( int x ,inty )
return dep[x] < dep[y] ?x : y ;
}
int c_query( int x ,inty )
if( x==y )return
ans;
if( dfn[x] >dfn[y] )swap( x ,y );
if(dfn[x] != dfn[y])ans += query( 1
,dfn[son[x]] ,dfn[y] );
return
ans ;
}
線段樹: 網上關於樹鏈剖分的**線段樹大多都是以結構體形式寫的:
structtreetree[n
<<2];
難道這樣更快?不懂(可能這個問題我永遠也不會搞懂
模板題:hdu 3966 aragorn's
story
區間修改,單點查詢
這個我線段樹沒用結構體寫也過了
#include#define fi first#define se second
#define rep( i ,x ,y ) for( int i= x; i<= y ;i++ )
#define reb( i ,y ,x ) for( int i= y; i>= x ;i-- )
#define mem( a ,x ) memset( a ,x ,sizeof(a))
#define lson pos<<1 ,l ,m
#define rson pos<<1|1 ,m+1 ,r
using
namespace
std;
typedef
long
long
ll;typedef
long
double
ld;typedef pair
pii;
typedef pair
pll;
typedef pair
psi;
const
int inf = 0x3f3f3f3f
;const
int n = 50005
;const
int m = 1000005
;struct
edgeedge[n
<<2
];int
sz[n] ,top[n] ,son[n] ,dep[n];
intfa[n] ,dfn[n] ,rnk[n];
int m ,n ,q ,tot = 0 ,cnt = 0
,k;int
head[n] ;
ll val[n
<<2] ,tree[n<<4] ,lazy[n<<4
];//
線段樹部分
void build( int pos ,int l ,int
r )
int m = (l+r)>>1
; build ( rson );
build ( lson );
tree[pos] = tree[pos<<1]+tree[pos<<1|1
];
return;}
void push_down( int
pos )
void updata( int pos ,int l, int r ,int l ,int r ,int
k )
int m = ( l+r )>>1
;
if( l <=m )updata( lson ,l ,r ,k);
if( r >m )updata( rson ,l ,r ,k);
tree[pos] = tree[pos>>1] + tree[pos>>1|1
];
return;}
ll query(
int pos ,int l ,int r ,int l ,int
r )//
樹鏈剖分部分 --dfs1 ,dfs2 ,query ,updata
void dfs1( int u ,int rt ,int
d ) }
return;}
void dfs2( int u ,int
t ) dfs2( son[u] ,t );
for( int i = head[u]; i ; i =edge[i].next )
}ll query_path(
int x ,int
y)
else
fx = top[x] ,fy =top[y];
}if(x !=y )
else
} else ans += query( 1 ,1
,n,dfn[x] ,dfn[y] );
return
ans;
}void updata_path( int x ,int y ,int
z )
else
fx = top[x] ,fy =top[y];
}if( x!=y )
else updata( 1 ,1
, n, dfn[x] ,dfn[y] ,z);
}int
main( )
dfs1(
1 ,1 ,0
); dfs2(
1 ,1
); build(
1, 1
,n );
char
op;
while ( q--)
if( op == 'q'
)
if( op == 'd'
) }
}return0;
}
樹鏈剖分 樹鏈剖分講解
好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...
演算法入門 樹鏈剖分 輕重鏈剖分
目錄 3.0 求 lca 4.0 利用資料結構維護資訊 5.0 例題 參考資料 資料結構入門 線段樹 發表於 2019 11 28 20 39 dfkuaid 摘要 線段樹的基本 建樹 區間查詢 單點修改 及高階操作 區間修改 單點查詢 區間修改 區間查詢 標記下傳 標記永久化 閱讀全文 樹鏈剖分用...
樹鏈剖分 樹剖換根
這是一道模板題。給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有乙個給定的權值。下面依次進行 m 個操作,操作分為如下五種型別 換根 將乙個指定的節點設定為樹的新根。修改路徑權值 給定兩個節點,將這兩個節點間路徑上的所有節點權值 含這兩個節點 增加乙個給定的值。修改子樹權值 給定乙...