Heavy Light Decomposition
(tree/heavy_light_decomposition.hpp)
説明
木をHLDする。パスクエリ、部分木クエリを処理することができる。
コンストラクタ
HeavyLightDecomposition(std::vector<std::vector<int>> g, int root = 0)
木グラフ g と root ノード番号を与えてHLDする。デフォルトで root は頂点 0
idx(int u)
頂点 $u$ のDFS行きがけ順の番号を返す。このidxの位置にデータ構造のインデックスを対応させればパスクエリや部分木クエリを処理することができる。具体的には使い方を参照。
la(int v, int k)
頂点 $v$ から $k$ だけ親方向に上った頂点を返す。 $O(\log N)$
lca(int u, int v)
頂点 $u$, $v$ のLCAを返す。 $O(\log N)$
jump(int s, int t, int k)
頂点 $s$ から $t$ の方向に $k$ だけ移動した頂点を返す。 $O(\log N)$
path(int s, int t)
頂点 $s$, $t$ の単純パスを返す。
parent(int u)
頂点 $u$ の親を返す。 $u$ が根である場合は、 $-1$ を返す。
distance(int u, int v)
頂点 $u$, $v$ の距離を返す。 $O(\log N)$
at_path(int u, int v, int s)
$u-v$ パスに $s$ が含まれるか判定。 $O(\log N)$
root_of_heavy_path(int u)
$u$ の属するheavy pathの最も親に近いものを返す。 $O(1)$
path_noncommutative_query(int u, int v, bool vertex, const F &f)
パス $u-v$ にクエリf
を適用する。非可換。vertexがtrueのとき、頂点に属性がある。vertexがfalseのとき、辺に属性がある。親-子間の辺属性は子のidxに持つ。
max_path(int u, int v, bool vertex, F binary_search)
パス $u-v$ に含まれる頂点で、 $u-m$ での値が true
を返す最大のパスとなる $m$ を二分探索で求める。
binary_search
という関数を引数に渡すが、これは path_noncommutative_query
で渡す f
と同じ引数を取り、返り値をデータ構造の二分探索した後の id
とする。
計算量はデータ構造に対する二分探索の計算量を $O(\log N)$ とすると $O((\log N)^2)$
subtree_query(int u, bool vertex, const F &f)
頂点 $u$ の部分木にクエリf
を適用する。vertexがtrueのとき、根頂点である $u$ にもクエリを適用する。
使い方
int main() {
int n, m;
std::cin >> n >> m;
std::vector<std::vector<int>> g(n);
rep(i,0,m) {
int u,v;
std::cin >> u >> v;
g[u].emplace_back(v);
g[v].emplace_back(u);
}
lib::HeavyLightDecomposition hld(g);
segtree<S, op, e> seg1(n)
segtree<S, op_rev, e> seg2(n);
auto set = [&](int u, S x) {
int idx = hld.idx(u);
seg1.set(idx, x);
seg2.set(idx, x);
};
S ans = e();
auto f = [&](int l, int r) {
if(l <= r) ans = op(ans, seg1.prod(l, r));
else ans = op(ans, seg2.prod(r, l));
};
int u,v;
std::cin >> u >> v;
ans = e();
hld.path_noncommutative_query(u, v, true, f);
std::cout << ans << '\n';
}
Depends on
Required by
Verified with
Code
#pragma once
#include <algorithm>
#include <cassert>
#include <vector>
#include "../graph/base.hpp"
namespace ebi {
template < class T > struct heavy_light_decomposition {
private:
void dfs_sz ( int v , Graph < T > & g ) {
for ( auto & e : g [ v ]) {
if ( e . to == par [ v ]) continue ;
par [ e . to ] = v ;
depth_ [ e . to ] = depth_ [ v ] + 1 ;
dist [ e . to ] = dist [ v ] + e . cost ;
dfs_sz ( e . to , g );
sz [ v ] += sz [ e . to ];
if ( sz [ e . to ] > sz [ g [ v ][ 0 ]. to ] || g [ v ][ 0 ]. to == par [ v ])
std :: swap ( e , g [ v ][ 0 ]);
}
}
void dfs_hld ( int v , const Graph < T > & g ) {
in [ v ] = num ++ ;
rev [ in [ v ]] = v ;
for ( auto e : g [ v ]) {
if ( e . to == par [ v ]) continue ;
nxt [ e . to ] = ( e . to == g [ v ][ 0 ]. to ? nxt [ v ] : e . to );
dfs_hld ( e . to , g );
}
out [ v ] = num ;
}
// [u, v) パスの取得 (v は u の祖先)
std :: vector < std :: pair < int , int >> ascend ( int u , int v ) const {
std :: vector < std :: pair < int , int >> res ;
while ( nxt [ u ] != nxt [ v ]) {
res . emplace_back ( in [ u ], in [ nxt [ u ]]);
u = par [ nxt [ u ]];
}
if ( u != v ) res . emplace_back ( in [ u ], in [ v ] + 1 );
return res ;
}
// (u, v] パスの取得 (u は v の祖先)
std :: vector < std :: pair < int , int >> descend ( int u , int v ) const {
if ( u == v ) return {};
if ( nxt [ u ] == nxt [ v ]) return {{ in [ u ] + 1 , in [ v ]}};
auto res = descend ( u , par [ nxt [ v ]]);
res . emplace_back ( in [ nxt [ v ]], in [ v ]);
return res ;
}
public:
heavy_light_decomposition ( Graph < T > gh , int root = 0 )
: n ( gh . size ()),
sz ( n , 1 ),
in ( n ),
out ( n ),
nxt ( n ),
par ( n , - 1 ),
depth_ ( n , 0 ),
rev ( n ),
dist ( n , 0 ) {
nxt [ root ] = root ;
dfs_sz ( root , gh );
dfs_hld ( root , gh );
}
int idx ( int u ) const {
return in [ u ];
}
int rev_idx ( int i ) const {
return rev [ i ];
}
int la ( int v , int k ) const {
while ( 1 ) {
int u = nxt [ v ];
if ( in [ u ] <= in [ v ] - k ) return rev [ in [ v ] - k ];
k -= in [ v ] - in [ u ] + 1 ;
v = par [ u ];
}
}
int lca ( int u , int v ) const {
while ( nxt [ u ] != nxt [ v ]) {
if ( in [ u ] < in [ v ]) std :: swap ( u , v );
u = par [ nxt [ u ]];
}
return depth_ [ u ] < depth_ [ v ] ? u : v ;
}
int jump ( int s , int t , int i ) const {
if ( i == 0 ) return s ;
int l = lca ( s , t );
int d = depth_ [ s ] + depth_ [ t ] - depth_ [ l ] * 2 ;
if ( d < i ) return - 1 ;
if ( depth_ [ s ] - depth_ [ l ] >= i ) return la ( s , i );
i = d - i ;
return la ( t , i );
}
std :: vector < int > path ( int s , int t ) const {
int l = lca ( s , t );
std :: vector < int > a , b ;
for (; s != l ; s = par [ s ]) a . emplace_back ( s );
for (; t != l ; t = par [ t ]) b . emplace_back ( t );
a . emplace_back ( l );
std :: reverse ( b . begin (), b . end ());
a . insert ( a . end (), b . begin (), b . end ());
return a ;
}
int root_of_heavy_path ( int u ) const {
return nxt [ u ];
}
int parent ( int u ) const {
return par [ u ];
}
T distance ( int u , int v ) const {
return dist [ u ] + dist [ v ] - 2 * dist [ lca ( u , v )];
}
T distance_from_root ( int v ) const {
return dist [ v ];
}
T depth ( int v ) const {
return depth_ [ v ];
}
bool at_path ( int u , int v , int s ) const {
return distance ( u , v ) == distance ( u , s ) + distance ( s , v );
}
template < class F >
void path_noncommutative_query ( int u , int v , bool vertex ,
const F & f ) const {
int l = lca ( u , v );
for ( auto [ a , b ] : ascend ( u , l )) f ( a + 1 , b );
if ( vertex ) f ( in [ l ], in [ l ] + 1 );
for ( auto [ a , b ] : descend ( l , v )) f ( a , b + 1 );
}
std :: vector < std :: pair < int , int >> path_sections ( int u , int v ,
bool vertex ) const {
int l = lca ( u , v );
std :: vector < std :: pair < int , int >> sections ;
for ( auto [ a , b ] : ascend ( u , l )) sections . emplace_back ( a + 1 , b );
if ( vertex ) sections . emplace_back ( in [ l ], in [ l ] + 1 );
for ( auto [ a , b ] : descend ( l , v )) sections . emplace_back ( a , b + 1 );
return sections ;
}
template < class F >
int max_path ( int u , int v , bool vertex , F binary_search ) const {
int prev = - 1 ;
int l = lca ( u , v );
for ( auto [ a , b ] : ascend ( u , l )) {
a ++ ;
int m = binary_search ( a , b );
if ( m == b ) {
prev = rev [ b ];
} else {
return ( m == a ? prev : rev [ m ]);
}
}
if ( vertex ) {
int m = binary_search ( in [ l ], in [ l ] + 1 );
if ( m == in [ l ]) {
return prev ;
} else {
prev = l ;
}
}
for ( auto [ a , b ] : descend ( l , v )) {
b ++ ;
int m = binary_search ( a , b );
if ( m == b ) {
prev = rev [ b - 1 ];
} else {
return m == a ? prev : rev [ m - 1 ];
}
}
return v ;
}
template < class F > void subtree_query ( int u , bool vertex , const F & f ) {
f ( in [ u ] + int ( ! vertex ), out [ u ]);
}
const std :: vector < int > & dfs_order () const {
return rev ;
}
std :: vector < std :: pair < int , int >> lca_based_auxiliary_tree_dfs_order (
std :: vector < int > vs ) const ;
std :: pair < std :: vector < int > , Graph < T >> lca_based_auxiliary_tree (
std :: vector < int > vs ) const ;
private:
int n ;
std :: vector < int > sz , in , out , nxt , par , depth_ , rev ;
std :: vector < T > dist ;
int num = 0 ;
};
} // namespace ebi
#line 2 "tree/heavy_light_decomposition.hpp"
#include <algorithm>
#include <cassert>
#include <vector>
#line 2 "graph/base.hpp"
#line 4 "graph/base.hpp"
#include <iostream>
#include <ranges>
#line 7 "graph/base.hpp"
#line 2 "data_structure/simple_csr.hpp"
#line 4 "data_structure/simple_csr.hpp"
#include <utility>
#line 6 "data_structure/simple_csr.hpp"
namespace ebi {
template < class E > struct simple_csr {
simple_csr () = default ;
simple_csr ( int n , const std :: vector < std :: pair < int , E >>& elements )
: start ( n + 1 , 0 ), elist ( elements . size ()) {
for ( auto e : elements ) {
start [ e . first + 1 ] ++ ;
}
for ( auto i : std :: views :: iota ( 0 , n )) {
start [ i + 1 ] += start [ i ];
}
auto counter = start ;
for ( auto [ i , e ] : elements ) {
elist [ counter [ i ] ++ ] = e ;
}
}
simple_csr ( const std :: vector < std :: vector < E >>& es )
: start ( es . size () + 1 , 0 ) {
int n = es . size ();
for ( auto i : std :: views :: iota ( 0 , n )) {
start [ i + 1 ] = ( int ) es [ i ]. size () + start [ i ];
}
elist . resize ( start . back ());
for ( auto i : std :: views :: iota ( 0 , n )) {
std :: copy ( es [ i ]. begin (), es [ i ]. end (), elist . begin () + start [ i ]);
}
}
int size () const {
return ( int ) start . size () - 1 ;
}
const auto operator []( int i ) const {
return std :: ranges :: subrange ( elist . begin () + start [ i ],
elist . begin () + start [ i + 1 ]);
}
auto operator []( int i ) {
return std :: ranges :: subrange ( elist . begin () + start [ i ],
elist . begin () + start [ i + 1 ]);
}
const auto operator ()( int i , int l , int r ) const {
return std :: ranges :: subrange ( elist . begin () + start [ i ] + l ,
elist . begin () + start [ i + 1 ] + r );
}
auto operator ()( int i , int l , int r ) {
return std :: ranges :: subrange ( elist . begin () + start [ i ] + l ,
elist . begin () + start [ i + 1 ] + r );
}
private:
std :: vector < int > start ;
std :: vector < E > elist ;
};
} // namespace ebi
#line 9 "graph/base.hpp"
namespace ebi {
template < class T > struct Edge {
int from , to ;
T cost ;
int id ;
};
template < class E > struct Graph {
using cost_type = E ;
using edge_type = Edge < cost_type > ;
Graph ( int n_ ) : n ( n_ ) {}
Graph () = default ;
void add_edge ( int u , int v , cost_type c ) {
buff . emplace_back ( u , edge_type { u , v , c , m });
edges . emplace_back ( edge_type { u , v , c , m ++ });
}
void add_undirected_edge ( int u , int v , cost_type c ) {
buff . emplace_back ( u , edge_type { u , v , c , m });
buff . emplace_back ( v , edge_type { v , u , c , m });
edges . emplace_back ( edge_type { u , v , c , m });
m ++ ;
}
void read_tree ( int offset = 1 , bool is_weighted = false ) {
read_graph ( n - 1 , offset , false , is_weighted );
}
void read_parents ( int offset = 1 ) {
for ( auto i : std :: views :: iota ( 1 , n )) {
int p ;
std :: cin >> p ;
p -= offset ;
add_undirected_edge ( p , i , 1 );
}
build ();
}
void read_graph ( int e , int offset = 1 , bool is_directed = false ,
bool is_weighted = false ) {
for ( int i = 0 ; i < e ; i ++ ) {
int u , v ;
std :: cin >> u >> v ;
u -= offset ;
v -= offset ;
if ( is_weighted ) {
cost_type c ;
std :: cin >> c ;
if ( is_directed ) {
add_edge ( u , v , c );
} else {
add_undirected_edge ( u , v , c );
}
} else {
if ( is_directed ) {
add_edge ( u , v , 1 );
} else {
add_undirected_edge ( u , v , 1 );
}
}
}
build ();
}
void build () {
assert ( ! prepared );
csr = simple_csr < edge_type > ( n , buff );
buff . clear ();
prepared = true ;
}
int size () const {
return n ;
}
int node_number () const {
return n ;
}
int edge_number () const {
return m ;
}
edge_type get_edge ( int i ) const {
return edges [ i ];
}
std :: vector < edge_type > get_edges () const {
return edges ;
}
const auto operator []( int i ) const {
return csr [ i ];
}
auto operator []( int i ) {
return csr [ i ];
}
private:
int n , m = 0 ;
std :: vector < std :: pair < int , edge_type >> buff ;
std :: vector < edge_type > edges ;
simple_csr < edge_type > csr ;
bool prepared = false ;
};
} // namespace ebi
#line 8 "tree/heavy_light_decomposition.hpp"
namespace ebi {
template < class T > struct heavy_light_decomposition {
private:
void dfs_sz ( int v , Graph < T > & g ) {
for ( auto & e : g [ v ]) {
if ( e . to == par [ v ]) continue ;
par [ e . to ] = v ;
depth_ [ e . to ] = depth_ [ v ] + 1 ;
dist [ e . to ] = dist [ v ] + e . cost ;
dfs_sz ( e . to , g );
sz [ v ] += sz [ e . to ];
if ( sz [ e . to ] > sz [ g [ v ][ 0 ]. to ] || g [ v ][ 0 ]. to == par [ v ])
std :: swap ( e , g [ v ][ 0 ]);
}
}
void dfs_hld ( int v , const Graph < T > & g ) {
in [ v ] = num ++ ;
rev [ in [ v ]] = v ;
for ( auto e : g [ v ]) {
if ( e . to == par [ v ]) continue ;
nxt [ e . to ] = ( e . to == g [ v ][ 0 ]. to ? nxt [ v ] : e . to );
dfs_hld ( e . to , g );
}
out [ v ] = num ;
}
// [u, v) パスの取得 (v は u の祖先)
std :: vector < std :: pair < int , int >> ascend ( int u , int v ) const {
std :: vector < std :: pair < int , int >> res ;
while ( nxt [ u ] != nxt [ v ]) {
res . emplace_back ( in [ u ], in [ nxt [ u ]]);
u = par [ nxt [ u ]];
}
if ( u != v ) res . emplace_back ( in [ u ], in [ v ] + 1 );
return res ;
}
// (u, v] パスの取得 (u は v の祖先)
std :: vector < std :: pair < int , int >> descend ( int u , int v ) const {
if ( u == v ) return {};
if ( nxt [ u ] == nxt [ v ]) return {{ in [ u ] + 1 , in [ v ]}};
auto res = descend ( u , par [ nxt [ v ]]);
res . emplace_back ( in [ nxt [ v ]], in [ v ]);
return res ;
}
public:
heavy_light_decomposition ( Graph < T > gh , int root = 0 )
: n ( gh . size ()),
sz ( n , 1 ),
in ( n ),
out ( n ),
nxt ( n ),
par ( n , - 1 ),
depth_ ( n , 0 ),
rev ( n ),
dist ( n , 0 ) {
nxt [ root ] = root ;
dfs_sz ( root , gh );
dfs_hld ( root , gh );
}
int idx ( int u ) const {
return in [ u ];
}
int rev_idx ( int i ) const {
return rev [ i ];
}
int la ( int v , int k ) const {
while ( 1 ) {
int u = nxt [ v ];
if ( in [ u ] <= in [ v ] - k ) return rev [ in [ v ] - k ];
k -= in [ v ] - in [ u ] + 1 ;
v = par [ u ];
}
}
int lca ( int u , int v ) const {
while ( nxt [ u ] != nxt [ v ]) {
if ( in [ u ] < in [ v ]) std :: swap ( u , v );
u = par [ nxt [ u ]];
}
return depth_ [ u ] < depth_ [ v ] ? u : v ;
}
int jump ( int s , int t , int i ) const {
if ( i == 0 ) return s ;
int l = lca ( s , t );
int d = depth_ [ s ] + depth_ [ t ] - depth_ [ l ] * 2 ;
if ( d < i ) return - 1 ;
if ( depth_ [ s ] - depth_ [ l ] >= i ) return la ( s , i );
i = d - i ;
return la ( t , i );
}
std :: vector < int > path ( int s , int t ) const {
int l = lca ( s , t );
std :: vector < int > a , b ;
for (; s != l ; s = par [ s ]) a . emplace_back ( s );
for (; t != l ; t = par [ t ]) b . emplace_back ( t );
a . emplace_back ( l );
std :: reverse ( b . begin (), b . end ());
a . insert ( a . end (), b . begin (), b . end ());
return a ;
}
int root_of_heavy_path ( int u ) const {
return nxt [ u ];
}
int parent ( int u ) const {
return par [ u ];
}
T distance ( int u , int v ) const {
return dist [ u ] + dist [ v ] - 2 * dist [ lca ( u , v )];
}
T distance_from_root ( int v ) const {
return dist [ v ];
}
T depth ( int v ) const {
return depth_ [ v ];
}
bool at_path ( int u , int v , int s ) const {
return distance ( u , v ) == distance ( u , s ) + distance ( s , v );
}
template < class F >
void path_noncommutative_query ( int u , int v , bool vertex ,
const F & f ) const {
int l = lca ( u , v );
for ( auto [ a , b ] : ascend ( u , l )) f ( a + 1 , b );
if ( vertex ) f ( in [ l ], in [ l ] + 1 );
for ( auto [ a , b ] : descend ( l , v )) f ( a , b + 1 );
}
std :: vector < std :: pair < int , int >> path_sections ( int u , int v ,
bool vertex ) const {
int l = lca ( u , v );
std :: vector < std :: pair < int , int >> sections ;
for ( auto [ a , b ] : ascend ( u , l )) sections . emplace_back ( a + 1 , b );
if ( vertex ) sections . emplace_back ( in [ l ], in [ l ] + 1 );
for ( auto [ a , b ] : descend ( l , v )) sections . emplace_back ( a , b + 1 );
return sections ;
}
template < class F >
int max_path ( int u , int v , bool vertex , F binary_search ) const {
int prev = - 1 ;
int l = lca ( u , v );
for ( auto [ a , b ] : ascend ( u , l )) {
a ++ ;
int m = binary_search ( a , b );
if ( m == b ) {
prev = rev [ b ];
} else {
return ( m == a ? prev : rev [ m ]);
}
}
if ( vertex ) {
int m = binary_search ( in [ l ], in [ l ] + 1 );
if ( m == in [ l ]) {
return prev ;
} else {
prev = l ;
}
}
for ( auto [ a , b ] : descend ( l , v )) {
b ++ ;
int m = binary_search ( a , b );
if ( m == b ) {
prev = rev [ b - 1 ];
} else {
return m == a ? prev : rev [ m - 1 ];
}
}
return v ;
}
template < class F > void subtree_query ( int u , bool vertex , const F & f ) {
f ( in [ u ] + int ( ! vertex ), out [ u ]);
}
const std :: vector < int > & dfs_order () const {
return rev ;
}
std :: vector < std :: pair < int , int >> lca_based_auxiliary_tree_dfs_order (
std :: vector < int > vs ) const ;
std :: pair < std :: vector < int > , Graph < T >> lca_based_auxiliary_tree (
std :: vector < int > vs ) const ;
private:
int n ;
std :: vector < int > sz , in , out , nxt , par , depth_ , rev ;
std :: vector < T > dist ;
int num = 0 ;
};
} // namespace ebi
Back to top page