Graph All
(graph/all.hpp)
- View this file on GitHub
- Last update: 2026-08-29 18:27:41+09:00
- Include:
#include "graph/all.hpp"
Overview
graph/all.hpp includes the general graph bundle plus the tree and flow
subcategories. It is convenient when writing quickly during a contest and you do
not want to manage individual graph includes.
Public namespaces stay flat and short: general graph helpers use
m1une::graph, tree helpers use m1une::tree, and flow helpers use
m1une::flow.
Included Headers
| Header | Graph orientation | Contents |
|---|---|---|
graph/graph.hpp |
Container |
Graph<T> and Edge<T> adjacency-list container. |
graph/dag.hpp |
Directed DAG only | Ordering, shortest and longest paths, path counts, reachability, transitive reduction, and minimum path cover. |
graph/tree/all.hpp |
Tree algorithms | Euler tours, rooted-tree metadata, LCA, HLD, Mo path queries, distance frequencies, Cartesian trees, virtual trees, tree hashing, rerooting DP, static top trees, and centroid decomposition. |
graph/flow/flow.hpp |
Flow networks | Max flow, min-cost flow, bounded flow, bounded min-cost flow, and Gomory-Hu trees. |
graph/counting.hpp |
Counting formulas | Counts common labeled graph classes, tournaments, DAGs, and unlabeled trees by vertex number. |
graph/matrix_tree_theorem.hpp |
Undirected or directed | Counts weighted spanning trees and rooted arborescences by determinant. |
graph/range_edge_graph.hpp |
Directed graph builder | Compact point-to-range, range-to-point, and range-to-range edges using segment trees. |
graph/shortest_path.hpp |
Direction-respecting / DAG-specific | Cow game difference constraints, BFS, 0-1 BFS, DAG shortest path, Dijkstra, k-shortest walk, Bellman-Ford, and Warshall-Floyd. |
graph/directed.hpp |
Directed-oriented bundle | Directed algorithms plus shortest paths. |
graph/undirected.hpp |
Undirected-oriented bundle | Undirected algorithms plus shortest paths and grid helpers. |
graph/bfs.hpp |
Direction-respecting | Unweighted shortest paths. |
graph/dfs.hpp |
Direction-respecting | Iterative DFS forests with parent paths, timestamps, and traversal orders. |
graph/zero_one_bfs.hpp |
Direction-respecting | Shortest paths with edge costs 0 or 1. |
graph/dag_shortest_path.hpp |
Directed DAG only | Shortest paths in a DAG, including negative edge costs. |
graph/dijkstra.hpp |
Direction-respecting | Non-negative weighted shortest paths. |
graph/k_shortest_walk.hpp |
Direction-respecting | The first k walk lengths with non-negative edge costs. |
graph/bellman_ford.hpp |
Direction-respecting | Shortest paths with negative edges and negative-cycle marking. |
graph/cow_game.hpp |
Difference constraints | Feasibility, assignments, and tight bounds for systems of difference inequalities. |
graph/warshall_floyd.hpp |
Direction-respecting | All-pairs shortest paths. |
graph/grid.hpp |
Undirected graph builder | Helper for converting 2D grid cells to graph vertex ids. |
graph/topological_sort.hpp |
Directed only | DAG ordering and cycle check. |
graph/dag_longest_path.hpp |
Directed DAG only | Maximum-cost paths from one or more sources with reconstruction. |
graph/dag_path_count.hpp |
Directed DAG only | Path counts from one or more sources. |
graph/dag_reachability.hpp |
Directed DAG only | Batched reachability queries and transitive reduction. |
graph/dag_path_cover.hpp |
Directed DAG only | Minimum vertex-disjoint path cover. |
graph/dominator_tree.hpp |
Directed rooted graph | Lengauer-Tarjan immediate dominators and dominator tree. |
graph/directed_mst.hpp |
Directed rooted graph | Minimum-cost spanning arborescence with edge reconstruction. |
graph/scc.hpp |
Directed only | Strongly connected components and condensation DAG. |
graph/incremental_scc.hpp |
Directed only | Offline SCC merge times under edge insertions. |
graph/functional_graph.hpp |
One successor per vertex | Cycle decomposition, large jumps, paths and orbits, visit counts, reachability distances, and synchronized meetings. |
graph/two_sat.hpp |
Implication graph | 2-SAT clauses, satisfiability, and one assignment. |
graph/lowlink.hpp |
Undirected only | Articulation points and bridges. |
graph/biconnected_components.hpp |
Undirected only | Vertex-biconnected blocks, articulation points, and block incidence. |
graph/block_cut_tree.hpp |
Undirected only | Block-cut forest and original-vertex-to-node mappings. |
graph/two_edge_connected_components.hpp |
Undirected only | Two-edge-connected components, bridges, and the contracted bridge forest. |
graph/three_edge_connected_components.hpp |
Undirected only | Linear-time three-edge-connected vertex decomposition. |
graph/st_numbering.hpp |
Undirected only | Bipolar numbering between specified source and sink vertices. |
graph/chordal_graph_recognition.hpp |
Direction ignored | Linear-time recognition with a perfect elimination ordering or induced-cycle certificate. |
graph/complement_connected_components.hpp |
Direction ignored | Linear-time connected components of the complement graph without constructing it. |
graph/bipartite.hpp |
Direction ignored / explicit bipartite sides | Two-colorability, matching, vertex/edge covers, independent sets, and optimal bipartite edge coloring. |
graph/general_matching.hpp |
Undirected only | Maximum-cardinality matching and minimum edge cover in general undirected graphs. |
graph/general_weighted_matching.hpp |
Undirected only | Maximum-total-weight matching in general undirected graphs. |
graph/maximum_clique.hpp |
Direction ignored | Exact maximum clique, maximum independent set, and minimum vertex cover with bitset branch-and-bound. |
graph/chromatic_number.hpp |
Direction ignored | Exact chromatic number for graphs with at most 20 vertices. |
graph/minimum_steiner_tree.hpp |
Undirected only | Exact edge- and vertex-weighted minimum Steiner-tree costs and reconstruction for a small terminal set. |
graph/replacement_paths.hpp |
Undirected positive-weight graphs | Edge- and vertex-failure replacement distances along one fixed shortest path. |
graph/namori.hpp |
Undirected Namori graph | Decomposes each unicyclic component into its cycle and attached rooted trees. |
graph/connected_components.hpp |
Direction ignored | Weak/ordinary connected components. |
graph/count_four_cycles.hpp |
Direction ignored | Counts four-cycles globally and for every edge, including parallel-edge choices. |
graph/cycle_detection.hpp |
Directed and undirected variants | Finds one cycle with the matching function. |
graph/enumerate_cliques.hpp |
Direction ignored | Enumerates every nonempty clique through a callback. |
graph/enumerate_triangles.hpp |
Direction ignored | Enumerates every triangle through a callback. |
graph/eulerian_trail.hpp |
Directed and undirected variants | Hierholzer Eulerian trails with original edge IDs. |
graph/kruskal.hpp |
Undirected only | Minimum spanning forest. |
Example
#include "graph/all.hpp"
#include <iostream>
int main() {
m1une::graph::Graph<long long> g(3);
g.add_directed_edge(0, 1, 10);
auto dist = m1une::graph::dijkstra(g, 0).dist;
std::cout << dist[1] << "\n";
}
Depends on
Mo's Algorithm
(algo/offline/mo.hpp)
DSU (Disjoint Set Union)
(ds/dsu/dsu.hpp)
Sparse Table
(ds/range_query/sparse_table.hpp)
Bellman-Ford
(graph/bellman_ford.hpp)
BFS
(graph/bfs.hpp)
Biconnected Components
(graph/biconnected_components.hpp)
Bipartite Graph
(graph/bipartite.hpp)
Block-Cut Tree
(graph/block_cut_tree.hpp)
Chordal Graph Recognition
(graph/chordal_graph_recognition.hpp)
Chromatic Number
(graph/chromatic_number.hpp)
Complement-Graph Connected Components
(graph/complement_connected_components.hpp)
Connected Components
(graph/connected_components.hpp)
Count Four Cycles
(graph/count_four_cycles.hpp)
Graph Counting
(graph/counting.hpp)
Cow Game (Difference Constraints)
(graph/cow_game.hpp)
Cycle Detection
(graph/cycle_detection.hpp)
DAG Algorithms
(graph/dag.hpp)
DAG Longest Path
(graph/dag_longest_path.hpp)
DAG Path Count
(graph/dag_path_count.hpp)
Minimum DAG Path Cover
(graph/dag_path_cover.hpp)
DAG Reachability and Transitive Reduction
(graph/dag_reachability.hpp)
DAG Shortest Path
(graph/dag_shortest_path.hpp)
DFS
(graph/dfs.hpp)
Dijkstra
(graph/dijkstra.hpp)
Directed Graph Algorithms
(graph/directed.hpp)
Directed Minimum Spanning Tree
(graph/directed_mst.hpp)
Dominator Tree
(graph/dominator_tree.hpp)
Enumerate Cliques
(graph/enumerate_cliques.hpp)
Enumerate Triangles
(graph/enumerate_triangles.hpp)
Eulerian Trail
(graph/eulerian_trail.hpp)
Bounded Flow
(graph/flow/bounded_flow.hpp)
Bounded Min Cost Flow
(graph/flow/bounded_min_cost_flow.hpp)
Flow
(graph/flow/flow.hpp)
Gomory-Hu Tree
(graph/flow/gomory_hu.hpp)
Max Flow
(graph/flow/max_flow.hpp)
Min Cost Flow
(graph/flow/min_cost_flow.hpp)
Functional Graph
(graph/functional_graph.hpp)
General Matching
(graph/general_matching.hpp)
General Weighted Matching
(graph/general_weighted_matching.hpp)
Graph
(graph/graph.hpp)
Graph
(graph/graph.hpp)
Grid
(graph/grid.hpp)
Incremental Strongly Connected Components
(graph/incremental_scc.hpp)
K-Shortest Walk
(graph/k_shortest_walk.hpp)
Kruskal
(graph/kruskal.hpp)
LowLink
(graph/lowlink.hpp)
Matrix-Tree Theorem
(graph/matrix_tree_theorem.hpp)
Maximum Clique, Independent Set, and Vertex Cover
(graph/maximum_clique.hpp)
Minimum Steiner Tree
(graph/minimum_steiner_tree.hpp)
Namori Graph Decomposition
(graph/namori.hpp)
Range Edge Graph
(graph/range_edge_graph.hpp)
Replacement Paths
(graph/replacement_paths.hpp)
Strongly Connected Components
(graph/scc.hpp)
Shortest Path
(graph/shortest_path.hpp)
st-Numbering
(graph/st_numbering.hpp)
Three-Edge-Connected Components
(graph/three_edge_connected_components.hpp)
Topological Sort
(graph/topological_sort.hpp)
Tree All
(graph/tree/all.hpp)
Cartesian Tree
(graph/tree/cartesian_tree.hpp)
Centroid Decomposition
(graph/tree/centroid_decomposition.hpp)
Tree Cumulative Sum
(graph/tree/cumulative_sum.hpp)
Tree Diameter
(graph/tree/diameter.hpp)
Tree Distance Frequency
(graph/tree/distance_frequency.hpp)
DSU on Tree
(graph/tree/dsu_on_tree.hpp)
Euler Tour
(graph/tree/euler_tour.hpp)
Heavy Light Decomposition
(graph/tree/heavy_light_decomposition.hpp)
Mo on Tree
(graph/tree/mo_on_tree.hpp)
Range Contour Query on Tree
(graph/tree/range_contour_query.hpp)
Rerooting DP
(graph/tree/rerooting_dp.hpp)
Rerooting Static Top Tree
(graph/tree/rerooting_static_top_tree.hpp)
Rooted Tree
(graph/tree/rooted_tree.hpp)
Sparse Table LCA
(graph/tree/sparse_table_lca.hpp)
Static Top Tree
(graph/tree/static_top_tree.hpp)
Tree
(graph/tree/tree.hpp)
Hash of Tree
(graph/tree/tree_hash.hpp)
Virtual Tree
(graph/tree/virtual_tree.hpp)
01 on Tree
(graph/tree/zero_one_on_tree.hpp)
Two-Edge-Connected Components
(graph/two_edge_connected_components.hpp)
Two-Satisfiability
(graph/two_sat.hpp)
Undirected Graph Algorithms
(graph/undirected.hpp)
Warshall-Floyd
(graph/warshall_floyd.hpp)
0-1 BFS
(graph/zero_one_bfs.hpp)
Combinatorics
(math/combinatorics.hpp)
Convolution
(math/fps/convolution.hpp)
Convolution
(math/fps/convolution.hpp)
Convolution
(math/fps/convolution.hpp)
Formal Power Series
(math/fps/formal_power_series.hpp)
math/fps/internal/ntt998_faster.hpp
Matrix Linear Algebra
(math/matrix/linear_algebra.hpp)
Dense Matrix
(math/matrix/matrix.hpp)
ModInt
(math/modint.hpp)
ModInt
(math/modint.hpp)
Modular Square Root
(math/modular_square_root.hpp)
Add Monoid
(monoid/add.hpp)
Monoid Concept
(monoid/concept.hpp)
Monoid Concept
(monoid/concept.hpp)
Dynamic Bitset
(utilities/dynamic_bitset.hpp)
Verified with
verify/graph/cow_game.test.cpp
verify/graph/graph_algorithms.test.cpp
verify/graph/range_edge_graph.test.cpp
Code
#ifndef M1UNE_GRAPH_ALL_HPP
#define M1UNE_GRAPH_ALL_HPP 1
#include "counting.hpp"
#include "directed.hpp"
#include "dominator_tree.hpp"
#include "flow/flow.hpp"
#include "graph.hpp"
#include "grid.hpp"
#include "range_edge_graph.hpp"
#include "replacement_paths.hpp"
#include "shortest_path.hpp"
#include "tree/all.hpp"
#include "undirected.hpp"
#endif // M1UNE_GRAPH_ALL_HPP#line 1 "graph/all.hpp"
#line 1 "graph/counting.hpp"
#include <cassert>
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
#line 1 "math/fps/convolution.hpp"
#include <algorithm>
#include <array>
#line 8 "math/fps/convolution.hpp"
#include <cstring>
#include <new>
#include <type_traits>
#line 13 "math/fps/convolution.hpp"
#if defined(__GNUC__) && !defined(__clang__) && \
(defined(__x86_64__) || defined(__i386__)) && \
!defined(M1UNE_FPS_DISABLE_X86_SIMD)
#include <immintrin.h>
#define M1UNE_FPS_HAS_X86_SIMD 1
#pragma GCC push_options
#pragma GCC target("avx2,bmi")
#endif
#line 1 "math/fps/internal/ntt998_faster.hpp"
#ifdef M1UNE_FPS_HAS_X86_SIMD
#line 9 "math/fps/internal/ntt998_faster.hpp"
#include <immintrin.h>
namespace m1une {
namespace fps {
namespace internal {
namespace fast998_v2 {
// Fixed-modulus AVX2 transform with an in-register degree-8 residue product.
using u32=unsigned;
using u64=unsigned long long;
using idt=std::size_t;
using I256=__m256i;
inline void store256(void*p,I256 x){
_mm256_store_si256((I256*)p,x);
}
inline I256 load256(const void*p){
return _mm256_load_si256((const I256*)p);
}
constexpr u32 shrk(u32 x,u32 M){
return std::min(x,x-M);
}
constexpr u32 dilt(u32 x,u32 M){
return std::min(x,x+M);
}
constexpr u32 reduce(u64 x,u32 niv,u32 M){
return (x+u64(u32(x)*niv)*M)>>32;
}
constexpr u32 mul(u32 x,u32 y,u32 niv,u32 M){
return reduce(u64(x)*y,niv,M);
}
constexpr u32 mul_s(u32 x,u32 y,u32 niv,u32 M){
return shrk(reduce(u64(x)*y,niv,M),M);
}
constexpr u32 qpw(u32 a,u32 b,u32 niv,u32 M,u32 r){
for(;b;b>>=1,a=mul(a,a,niv,M)){
if(b&1){
r=mul(r,a,niv,M);
}
}
return r;
}
constexpr u32 qpw_s(u32 a,u32 b,u32 niv,u32 M,u32 r){
return shrk(qpw(a,b,niv,M,r),M);
}
inline I256 shrk32(I256 x,I256 M){
return _mm256_min_epu32(x,_mm256_sub_epi32(x,M));
}
inline I256 dilt32(I256 x,I256 M){
return _mm256_min_epu32(x,_mm256_add_epi32(x,M));
}
inline I256 Ladd32(I256 x,I256 y,I256){
return _mm256_add_epi32(x,y);
}
inline I256 Lsub32(I256 x,I256 y,I256 M){
return _mm256_add_epi32(_mm256_sub_epi32(x,y),M);
}
inline I256 add32(I256 x,I256 y,I256 M){
return shrk32(_mm256_add_epi32(x,y),M);
}
inline I256 sub32(I256 x,I256 y,I256 M){
return dilt32(_mm256_sub_epi32(x,y),M);
}
template<int msk>inline I256 neg32_m(I256 x,I256 M){
return _mm256_blend_epi32(x,_mm256_sub_epi32(M,x),msk);
}
inline I256 reduce(I256 a,I256 b,I256 niv,I256 M){
I256 c=_mm256_mul_epu32(a,niv),d=_mm256_mul_epu32(b,niv);
c=_mm256_mul_epu32(c,M),d=_mm256_mul_epu32(d,M);
return _mm256_blend_epi32(_mm256_srli_epi64(_mm256_add_epi64(a,c),32),_mm256_add_epi64(b,d),0xaa);
}
inline I256 mul(I256 a,I256 b,I256 niv,I256 M){
return reduce(_mm256_mul_epu32(a,b),_mm256_mul_epu32(_mm256_srli_epi64(a,32),_mm256_srli_epi64(b,32)),niv,M);
}
inline I256 mul_s(I256 a,I256 b,I256 niv,I256 M){
return shrk32(mul(a,b,niv,M),M);
}
inline I256 mul_bsm(I256 a,I256 b,I256 niv,I256 M){
return reduce(_mm256_mul_epu32(a,b),_mm256_mul_epu32(_mm256_srli_epi64(a,32),b),niv,M);
}
inline I256 mul_bsmfxd(I256 a,I256 b,I256 bniv,I256 M){
I256 cc=_mm256_mul_epu32(a,bniv),dd=_mm256_mul_epu32(_mm256_srli_epi64(a,32),bniv);
I256 c=_mm256_mul_epu32(a,b),d=_mm256_mul_epu32(_mm256_srli_epi64(a,32),b);
cc=_mm256_mul_epu32(cc,M),dd=_mm256_mul_epu32(dd,M);
return _mm256_blend_epi32(_mm256_srli_epi64(_mm256_add_epi64(c,cc),32),_mm256_add_epi64(d,dd),0xaa);
}
inline I256 mul_bfxd(I256 a,I256 b,I256 bniv,I256 M){
I256 cc=_mm256_mul_epu32(a,bniv),dd=_mm256_mul_epu32(_mm256_srli_epi64(a,32),_mm256_srli_epi64(bniv,32));
I256 c=_mm256_mul_epu32(a,b),d=_mm256_mul_epu32(_mm256_srli_epi64(a,32),_mm256_srli_epi64(b,32));
cc=_mm256_mul_epu32(cc,M),dd=_mm256_mul_epu32(dd,M);
return _mm256_blend_epi32(_mm256_srli_epi64(_mm256_add_epi64(c,cc),32),_mm256_add_epi64(d,dd),0xaa);
}
inline I256 mul_upd_rt(I256 a,I256 bu,I256 M){
I256 cc=_mm256_mul_epu32(a,bu),c=_mm256_mul_epu32(a,_mm256_srli_epi64(bu,32));
cc=_mm256_mul_epu32(cc,M);
return shrk32(_mm256_srli_epi64(_mm256_add_epi64(c,cc),32),M);
}
constexpr auto _mxlg=26,_lg_itth=6;
constexpr auto _itth=idt(1)<<_lg_itth;
static_assert(_lg_itth%2==0);
struct FNTT32_info{
u32 mod,mod2,niv,one,r2,r3,img,imgniv,RT1[_mxlg];
alignas(32) std::array<u32,8> rt3[_mxlg-2],rt3i[_mxlg-2],bwbr,bwb,bwbi,rt4[_mxlg-3],rt4niv[_mxlg-3],rt4i[_mxlg-3],rt4iniv[_mxlg-3],pr2,pr4,pr2niv,pr4niv,pr2i,pr2iniv,pr4i,pr4iniv;
constexpr FNTT32_info(const u32 m):mod(m),mod2(m*2),niv([&]{u32 n=2+m;for(int i=0;i<4;++i){n*=2+m*n;}return n;}()),one((-m)%m),r2((-u64(m))%m),r3(mul_s(r2,r2,niv,m)),img{},imgniv{},RT1{},rt3{},rt3i{},bwbr{},bwb{},bwbi{},rt4{},rt4niv{},rt4i{},rt4iniv{},pr2{},pr4{},pr2niv{},pr4niv{},pr2i{},pr2iniv{},pr4i{},pr4iniv{}{
const int k=__builtin_ctz(m-1);
u32 _g=mul(3,r2,niv,mod);
for(;;++_g){
if(qpw_s(_g,mod>>1,niv,mod,one)!=one){
break;
}
}
_g=qpw(_g,mod>>k,niv,mod,one);
u32 rt1[_mxlg-1],rt1i[_mxlg-1];
rt1[k-2]=_g,rt1i[k-2]=qpw(_g,mod-2,niv,mod,one);
for(int i=k-2;i>0;--i){
rt1[i-1]=mul(rt1[i],rt1[i],niv,mod);
rt1i[i-1]=mul(rt1i[i],rt1i[i],niv,mod);
}
RT1[k-1]=qpw_s(_g,3,niv,mod,one);
for(int i=k-1;i>0;--i){
RT1[i-1]=mul_s(RT1[i],RT1[i],niv,mod);
}
img=rt1[0],imgniv=img*niv;
bwbr={one,0,one,0,one};
bwb={rt1[1],0,rt1[0],0,mod-mul_s(rt1[0],rt1[1],niv,mod)};
bwbi={rt1i[1],0,rt1i[0],0,mul_s(rt1i[0],rt1i[1],niv,mod)};
u32 pr=one,pri=one;
for(int i=0;i<k-2;++i){
const u32 r=mul_s(pr,rt1[i+1],niv,mod),ri=mul_s(pri,rt1i[i+1],niv,mod);
const u32 r2=mul_s(r,r,niv,mod),r2i=mul_s(ri,ri,niv,mod);
const u32 r3=mul_s(r,r2,niv,mod),r3i=mul_s(ri,r2i,niv,mod);
rt3[i]={r*niv,r,r2*niv,r2,r3*niv,r3};
rt3i[i]={ri*niv,ri,r2i*niv,r2i,r3i*niv,r3i};
pr=mul(pr,rt1i[i+1],niv,mod),pri=mul(pri,rt1[i+1],niv,mod);
}
pr=one,pri=one;
for(int i=0;i<k-3;++i){
const u32 r=mul_s(pr,rt1[i+2],niv,mod),ri=mul_s(pri,rt1i[i+2],niv,mod);
rt4[i][0]=rt4i[i][0]=one;
for(int j=1;j<8;++j){
rt4[i][j]=mul_s(rt4[i][j-1],r,niv,mod);
rt4i[i][j]=mul_s(rt4i[i][j-1],ri,niv,mod);
}
for(int j=0;j<8;++j){
rt4niv[i][j]=rt4[i][j]*niv;
rt4iniv[i][j]=rt4i[i][j]*niv;
}
pr=mul(pr,rt1i[i+2],niv,mod),pri=mul(pri,rt1[i+2],niv,mod);
}
pr2={one,one,one,img,one,one,one,img};
pr4={one,one,one,one,one,rt1[1],img,mul_s(img,rt1[1],niv,mod)};
const u32 nr2=mod-r2,imgr2=mul_s(img,r2,niv,mod);
pr2i={nr2,nr2,nr2,imgr2,nr2,nr2,nr2,imgr2};
pr4i={one,one,one,one,one,rt1i[1],rt1i[0],mul_s(rt1i[0],rt1i[1],niv,mod)};
for(int j=0;j<8;++j){
pr2niv[j]=pr2[j]*niv,pr4niv[j]=pr4[j]*niv;
pr2iniv[j]=pr2i[j]*niv,pr4iniv[j]=pr4i[j]*niv;
}
}
};
inline void vector_dif(I256*const f,const idt n,const FNTT32_info*info){
alignas(32) std::array<u32,8> st_1[_mxlg>>1];
const I256 Mod=_mm256_set1_epi32(info->mod),Mod2=_mm256_set1_epi32(info->mod2),Niv=_mm256_set1_epi32(info->niv);
const I256 Img=_mm256_set1_epi32(info->img),ImgNiv=_mm256_set1_epi32(info->imgniv),id=_mm256_setr_epi32(0,2,0,4,0,2,0,4);
const int lgn=__builtin_ctzll(n);
std::fill(st_1,st_1+(lgn>>1),info->bwb);
const idt nn=n>>(lgn&1),m=std::min(n,_itth),mm=std::min(nn,_itth);
// I256 rr=_mm256_set1_epi32(info->one);
if(nn!=n){
for(idt i=0;i<nn;++i){
auto const p0=f+i,p1=f+nn+i;
const auto f0=load256(p0),f1=load256(p1);
const auto g0=add32(f0,f1,Mod2),g1=Lsub32(f0,f1,Mod2);
store256(p0,g0),store256(p1,g1);
}
}
for(idt L=nn>>2;L>0;L>>=2){
for(idt i=0;i<L;++i){
auto const p0=f+i,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f1=load256(p1),f3=load256(p3),f2=load256(p2),f0=load256(p0);
const auto g3=mul_bsmfxd(Lsub32(f1,f3,Mod2),Img,ImgNiv,Mod),g1=add32(f1,f3,Mod2);
const auto g0=add32(f0,f2,Mod2),g2=sub32(f0,f2,Mod2);
const auto h0=add32(g0,g1,Mod2),h1=Lsub32(g0,g1,Mod2);
const auto h2=Ladd32(g2,g3,Mod2),h3=Lsub32(g2,g3,Mod2);
store256(p0,h0),store256(p1,h1),store256(p2,h2),store256(p3,h3);
}
}
for(idt j=0;j<n;j+=m){
int t=((j==0)?std::min(_lg_itth,lgn):__builtin_ctzll(j))&-2,p=(t-2)>>1;
for(idt L=(idt(1)<<t)>>2;L>=_itth;L>>=2,t-=2,--p){
auto rt=load256(st_1+p);
const auto r1=_mm256_permutevar8x32_epi32(rt,id);
const auto r1Niv=_mm256_permutevar8x32_epi32(_mm256_mul_epu32(rt,Niv),id);
rt=mul_upd_rt(rt,load256(info->rt3+__builtin_ctzll(~j>>t)),Mod);
const auto r2=_mm256_shuffle_epi32(r1,_MM_PERM_BBBB),nr3=_mm256_shuffle_epi32(r1,_MM_PERM_DDDD);
const auto r2Niv=_mm256_shuffle_epi32(r1Niv,_MM_PERM_BBBB),nr3Niv=_mm256_shuffle_epi32(r1Niv,_MM_PERM_DDDD);
store256(st_1+p,rt);
for(idt i=0;i<L;++i){
auto const p0=f+i+j,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f1=load256(p1),f3=load256(p3),f2=load256(p2),f0=load256(p0);
const auto g1=mul_bsmfxd(f1,r1,r1Niv,Mod),ng3=mul_bsmfxd(f3,nr3,nr3Niv,Mod);
const auto g2=mul_bsmfxd(f2,r2,r2Niv,Mod),g0=shrk32(f0,Mod2);
const auto h3=mul_bsmfxd(Ladd32(g1,ng3,Mod2),Img,ImgNiv,Mod),h1=sub32(g1,ng3,Mod2);
const auto h0=add32(g0,g2,Mod2),h2=sub32(g0,g2,Mod2);
const auto u0=Ladd32(h0,h1,Mod2),u1=Lsub32(h0,h1,Mod2);
const auto u2=Ladd32(h2,h3,Mod2),u3=Lsub32(h2,h3,Mod2);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
}
I256*const g=f+j;
for(idt l=mm,L=mm>>2;L;l=L,L>>=2,t-=2,--p){
auto rt=load256(st_1+p);
for(idt i=(j==0?l:0),k=(j+i)>>t;i<m;i+=l,++k){
const auto r1=_mm256_permutevar8x32_epi32(rt,id);
const auto r2=_mm256_shuffle_epi32(r1,_MM_PERM_BBBB);
const auto nr3=_mm256_shuffle_epi32(r1,_MM_PERM_DDDD);
for(idt j=0;j<L;++j){
auto const p0=g+i+j,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f1=load256(p1),f3=load256(p3),f2=load256(p2),f0=load256(p0);
const auto g1=mul_bsm(f1,r1,Niv,Mod),ng3=mul_bsm(f3,nr3,Niv,Mod);
const auto g2=mul_bsm(f2,r2,Niv,Mod),g0=shrk32(f0,Mod2);
const auto h3=mul_bsmfxd(Ladd32(g1,ng3,Mod2),Img,ImgNiv,Mod),h1=sub32(g1,ng3,Mod2);
const auto h0=add32(g0,g2,Mod2),h2=sub32(g0,g2,Mod2);
const auto u0=Ladd32(h0,h1,Mod2),u1=Lsub32(h0,h1,Mod2);
const auto u2=Ladd32(h2,h3,Mod2),u3=Lsub32(h2,h3,Mod2);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
rt=mul_upd_rt(rt,load256(info->rt3+__builtin_ctzll(~k)),Mod);
}
store256(st_1+p,rt);
}
// const auto pr2=load256(&info->pr2),pr4=load256(&info->pr4);
// const auto pr2Niv=load256(&info->pr2niv),pr4Niv=load256(&info->pr4niv);
// for(idt i=j;i<j+m;++i){
// auto fi=load256(f+i);
// fi=mul(fi,rr,Niv,Mod);
// rr=shrk32(mul_bfxd(rr,load256(info->rt4+__builtin_ctzll(~i)),load256(info->rt4niv+__builtin_ctzll(~i)),Mod),Mod);
// fi=mul_bfxd(Ladd32(neg32_m<0xf0>(fi,Mod2),_mm256_permute2x128_si256(fi,fi,1),Mod2),pr4,pr4Niv,Mod);
// fi=mul_bfxd(Ladd32(neg32_m<0xcc>(fi,Mod2),_mm256_shuffle_epi32(fi,0x4e),Mod2),pr2,pr2Niv,Mod);
// fi=sub32(_mm256_shuffle_epi32(fi,0xb1),neg32_m<0x55>(fi,Mod2),Mod2);
// store256(f+i,fi);
// }
}
}
template<bool shrk=false>inline void vector_dit(I256*const f,idt n,const FNTT32_info*const info){
alignas(32) std::array<u32,8> st_1[_mxlg>>1];
const I256 Mod=_mm256_set1_epi32(info->mod),Mod2=_mm256_set1_epi32(info->mod2),Niv=_mm256_set1_epi32(info->niv);
const I256 Img=_mm256_set1_epi32(info->img),ImgNiv=_mm256_set1_epi32(info->imgniv),id=_mm256_setr_epi32(0,2,0,4,0,2,0,4);
const int lgn=__builtin_ctzll(n);
std::fill(st_1,st_1+(_lg_itth>>1),info->bwbr);
std::fill(st_1+(_lg_itth>>1),st_1+(_mxlg>>1),info->bwbi);
const idt nn=n>>(lgn&1),mm=std::min(nn,_itth);
// I256 rr=_mm256_set1_epi32((info->mod-1)>>(lgn+3));
for(idt j=0;j<n;j+=mm){
// const auto pr2=load256(&info->pr2i),pr4=load256(&info->pr4i);
// const auto pr2Niv=load256(&info->pr2iniv),pr4Niv=load256(&info->pr4iniv);
// for(idt i=j;i<j+mm;++i){
// auto fi=load256(f+i);
// const auto rt=rr;
// rr=shrk32(mul_bfxd(rr,load256(info->rt4i+__builtin_ctzll(~i)),load256(info->rt4iniv+__builtin_ctzll(~i)),Mod),Mod);
// fi=mul_bfxd(Ladd32(neg32_m<0xaa>(fi,Mod2),_mm256_shuffle_epi32(fi,0xb1),Mod2),pr2,pr2Niv,Mod);
// fi=mul_bfxd(Ladd32(neg32_m<0xcc>(fi,Mod2),_mm256_shuffle_epi32(fi,0x4e),Mod2),pr4,pr4Niv,Mod);
// fi=mul(Ladd32(neg32_m<0xf0>(fi,Mod2),_mm256_permute2x128_si256(fi,fi,1),Mod2),rt,Niv,Mod);
// store256(f+i,fi);
// }
I256*const g=f+j;
int t=2,p=0;
for(idt l=4,L=1;l<=mm;L=l,l<<=2,t+=2,++p){
auto rt=load256(st_1+p);
for(idt i=0,k=j>>t;i<mm;i+=l,++k){
const auto r1=_mm256_permutevar8x32_epi32(rt,id);
const auto r2=_mm256_shuffle_epi32(r1,_MM_PERM_BBBB);
const auto r3=_mm256_shuffle_epi32(r1,_MM_PERM_DDDD);
for(idt j=0;j<L;++j){
auto const p0=g+i+j,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f0=load256(p0),f1=load256(p1),f2=load256(p2),f3=load256(p3);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
const auto g2=add32(f2,f3,Mod2),g3=mul_bsmfxd(Lsub32(f3,f2,Mod2),Img,ImgNiv,Mod);
const auto h0=Ladd32(g0,g2,Mod2),h1=Ladd32(g1,g3,Mod2);
const auto h2=Lsub32(g0,g2,Mod2),h3=Lsub32(g1,g3,Mod2);
const auto u0=shrk32(h0,Mod2),u1=mul_bsm(h1,r1,Niv,Mod);
const auto u2=mul_bsm(h2,r2,Niv,Mod),u3=mul_bsm(h3,r3,Niv,Mod);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
rt=mul_upd_rt(rt,load256(info->rt3i+__builtin_ctzll(~k)),Mod);
}
store256(st_1+p,rt);
}
int tt=std::min(__builtin_ctzll(~(j>>_lg_itth))+_lg_itth,lgn);
for(idt L=_itth,l=L<<2;t<=tt;L=l,l<<=2,t+=2,++p){
if((j+_itth)==l){
if(shrk && l==n){
for(idt i=0;i<L;++i){
auto const p0=f+i,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f2=load256(p2),f3=load256(p3),f0=load256(p0),f1=load256(p1);
const auto g3=mul_bsmfxd(Lsub32(f3,f2,Mod2),Img,ImgNiv,Mod),g2=add32(f2,f3,Mod2);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
const auto h0=add32(g0,g2,Mod2),h1=add32(g1,g3,Mod2);
const auto h2=sub32(g0,g2,Mod2),h3=sub32(g1,g3,Mod2);
const auto u0=shrk32(h0,Mod),u1=shrk32(h1,Mod);
const auto u2=shrk32(h2,Mod),u3=shrk32(h3,Mod);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
}
else{
for(idt i=0;i<L;++i){
auto const p0=f+i,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f2=load256(p2),f3=load256(p3),f0=load256(p0),f1=load256(p1);
const auto g3=mul_bsmfxd(Lsub32(f3,f2,Mod2),Img,ImgNiv,Mod),g2=add32(f2,f3,Mod2);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
const auto h0=add32(g0,g2,Mod2),h1=add32(g1,g3,Mod2);
const auto h2=sub32(g0,g2,Mod2),h3=sub32(g1,g3,Mod2);
store256(p0,h0),store256(p1,h1),store256(p2,h2),store256(p3,h3);
}
}
}
else{
auto rt=load256(st_1+p);
const auto r1=_mm256_permutevar8x32_epi32(rt,id);
const auto r1Niv=_mm256_permutevar8x32_epi32(_mm256_mul_epu32(rt,Niv),id);
rt=mul_upd_rt(rt,load256(info->rt3i+__builtin_ctzll(~j>>t)),Mod);
const auto r2=_mm256_shuffle_epi32(r1,_MM_PERM_BBBB),r3=_mm256_shuffle_epi32(r1,_MM_PERM_DDDD);
const auto r2Niv=_mm256_shuffle_epi32(r1Niv,_MM_PERM_BBBB),r3Niv=_mm256_shuffle_epi32(r1Niv,_MM_PERM_DDDD);
store256(st_1+p,rt);
for(idt i=0;i<L;++i){
auto const p0=f+j+_itth-l+i,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f0=load256(p0),f1=load256(p1),f2=load256(p2),f3=load256(p3);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
const auto g2=add32(f2,f3,Mod2),g3=mul_bsmfxd(Lsub32(f3,f2,Mod2),Img,ImgNiv,Mod);
const auto h0=Ladd32(g0,g2,Mod2),h1=Ladd32(g1,g3,Mod2);
const auto h2=Lsub32(g0,g2,Mod2),h3=Lsub32(g1,g3,Mod2);
const auto u0=shrk32(h0,Mod2),u1=mul_bsmfxd(h1,r1,r1Niv,Mod);
const auto u2=mul_bsmfxd(h2,r2,r2Niv,Mod),u3=mul_bsmfxd(h3,r3,r3Niv,Mod);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
}
}
}
if(shrk && nn==n && n<=_itth){
for(idt i=0;i<n;++i){
const auto f0=load256(f+i);
store256(f+i,shrk32(f0,Mod));
}
}
if(nn!=n){
for(idt i=0;i<nn;++i){
auto const p0=f+i,p1=f+nn+i;
const auto f0=load256(p0),f1=load256(p1);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
if constexpr(shrk){
const auto h0=shrk32(g0,Mod),h1=shrk32(g1,Mod);
store256(p0,h0),store256(p1,h1);
}
else{
store256(p0,g0),store256(p1,g1);
}
}
}
}
// Returns fx * f[0,8) * g[0,8) (mod x^8 - ww).
[[gnu::always_inline]] inline I256 convolve8(const I256*f,const I256*g,I256 ww,I256 fx,I256 Niv,I256 Mod,I256 Mod2){
const auto raa=load256(f),rbb=load256(g);
const auto taa=shrk32(raa,Mod2),bb=shrk32(mul_bsm(rbb,fx,Niv,Mod),Mod);
const auto aw=shrk32(mul_bsm(taa,ww,Niv,Mod),Mod);
const auto aa=shrk32(taa,Mod);
const auto awa=_mm256_permute2x128_si256(aa,aw,3);
const auto b0=_mm256_permute4x64_epi64(bb,0x00),b1=_mm256_shuffle_epi32(b0,_MM_PERM_CDAB);
const auto a0=aa,a1=_mm256_srli_epi64(a0,32);
const auto aw7=_mm256_alignr_epi8(aa,awa,12);
auto res00=_mm256_mul_epu32(a0,b0);
auto res01=_mm256_mul_epu32(a1,b0);
auto res10=_mm256_mul_epu32(aw7,b1);
auto res11=_mm256_mul_epu32(a0,b1);
const auto b2=_mm256_permute4x64_epi64(bb,0x55),b3=_mm256_shuffle_epi32(b2,_MM_PERM_CDAB);
const auto aw6=_mm256_alignr_epi8(aa,awa,8);
const auto aw5=_mm256_alignr_epi8(aa,awa,4);
res00=_mm256_add_epi64(res00,_mm256_mul_epu32(aw6,b2));
res01=_mm256_add_epi64(res01,_mm256_mul_epu32(aw7,b2));
res10=_mm256_add_epi64(res10,_mm256_mul_epu32(aw5,b3));
res11=_mm256_add_epi64(res11,_mm256_mul_epu32(aw6,b3));
const auto b4=_mm256_permute4x64_epi64(bb,0xaa),b5=_mm256_shuffle_epi32(b4,_MM_PERM_CDAB);
const auto aw3=_mm256_alignr_epi8(awa,aw,12);
res00=_mm256_add_epi64(res00,_mm256_mul_epu32(awa,b4));
res01=_mm256_add_epi64(res01,_mm256_mul_epu32(aw5,b4));
res10=_mm256_add_epi64(res10,_mm256_mul_epu32(aw3,b5));
res11=_mm256_add_epi64(res11,_mm256_mul_epu32(awa,b5));
const auto b6=_mm256_permute4x64_epi64(bb,0xff),b7=_mm256_shuffle_epi32(b6,_MM_PERM_CDAB);
const auto aw2=_mm256_alignr_epi8(awa,aw,8);
const auto aw1=_mm256_alignr_epi8(awa,aw,4);
res00=_mm256_add_epi64(res00,_mm256_mul_epu32(aw2,b6));
res01=_mm256_add_epi64(res01,_mm256_mul_epu32(aw3,b6));
res10=_mm256_add_epi64(res10,_mm256_mul_epu32(aw1,b7));
res11=_mm256_add_epi64(res11,_mm256_mul_epu32(aw2,b7));
res00=_mm256_add_epi64(res00,res10);
res01=_mm256_add_epi64(res01,res11);
return shrk32(reduce(res00,res01,Niv,Mod),Mod2);
}
inline void vector_convolution_direct(I256*f,const I256*g,idt lm,const FNTT32_info*const info){
u32 RR=info->one;
const auto mod=info->mod,niv=info->niv;
const auto Fx=_mm256_set1_epi32(mul_s((mod-((mod-1)>>(__builtin_ctzll(lm)))),info->r3,niv,mod));
const auto Niv=_mm256_set1_epi32(niv),Mod=_mm256_set1_epi32(mod),Mod2=_mm256_set1_epi32(info->mod2);
for(idt i=0;i<lm;++i){
store256(f+i,convolve8(f+i,g+i,_mm256_set1_epi32(RR),Fx,Niv,Mod,Mod2));
RR=mul(RR,info->RT1[__builtin_ctzll(~i)],niv,mod);
}
}
inline void vector_convolution_accumulate(I256*const result,const I256*const f,
const I256*const g,idt lm,
const FNTT32_info*const info){
u32 RR=info->one;
const auto mod=info->mod,niv=info->niv;
const auto Fx=_mm256_set1_epi32(mul_s((mod-((mod-1)>>(__builtin_ctzll(lm)))),info->r3,niv,mod));
const auto Niv=_mm256_set1_epi32(niv),Mod=_mm256_set1_epi32(mod),Mod2=_mm256_set1_epi32(info->mod2);
for(idt i=0;i<lm;++i){
const auto product=convolve8(f+i,g+i,_mm256_set1_epi32(RR),Fx,Niv,Mod,Mod2);
store256(result+i,add32(load256(result+i),product,Mod2));
RR=mul(RR,info->RT1[__builtin_ctzll(~i)],niv,mod);
}
}
} // namespace fast998_v2
} // namespace internal
} // namespace fps
} // namespace m1une
#endif // M1UNE_FPS_HAS_X86_SIMD
#line 24 "math/fps/convolution.hpp"
#ifdef M1UNE_FPS_HAS_X86_SIMD
#pragma GCC pop_options
#endif
#line 1 "math/modint.hpp"
#line 6 "math/modint.hpp"
#include <iostream>
#line 9 "math/modint.hpp"
namespace m1une {
namespace math {
template <uint32_t Modulus>
struct ModInt {
static_assert(0 < Modulus, "Modulus must be positive");
private:
uint32_t _v;
public:
static constexpr uint32_t mod() {
return Modulus;
}
static constexpr ModInt raw(uint32_t v) noexcept {
ModInt x;
x._v = v;
return x;
}
constexpr ModInt() noexcept : _v(0) {}
template <class Integer, std::enable_if_t<std::is_integral_v<Integer>, int> = 0>
constexpr ModInt(Integer v) noexcept {
if constexpr (std::is_signed_v<Integer>) {
int64_t x = static_cast<int64_t>(v) % static_cast<int64_t>(Modulus);
if (x < 0) x += Modulus;
_v = static_cast<uint32_t>(x);
} else {
_v = static_cast<uint32_t>(static_cast<uint64_t>(v) % Modulus);
}
}
constexpr uint32_t val() const noexcept {
return _v;
}
constexpr ModInt& operator++() noexcept {
_v++;
if (_v == Modulus) _v = 0;
return *this;
}
constexpr ModInt& operator--() noexcept {
if (_v == 0) _v = Modulus;
_v--;
return *this;
}
constexpr ModInt operator++(int) noexcept {
ModInt res = *this;
++*this;
return res;
}
constexpr ModInt operator--(int) noexcept {
ModInt res = *this;
--*this;
return res;
}
constexpr ModInt& operator+=(const ModInt& rhs) noexcept {
_v += rhs._v;
if (_v >= Modulus) _v -= Modulus;
return *this;
}
constexpr ModInt& operator-=(const ModInt& rhs) noexcept {
_v -= rhs._v;
if (_v >= Modulus) _v += Modulus;
return *this;
}
constexpr ModInt& operator*=(const ModInt& rhs) noexcept {
uint64_t z = _v;
z *= rhs._v;
_v = static_cast<uint32_t>(z % Modulus);
return *this;
}
constexpr ModInt& operator/=(const ModInt& rhs) noexcept {
return *this *= rhs.inv();
}
constexpr ModInt operator+(const ModInt& rhs) const noexcept {
return ModInt(*this) += rhs;
}
constexpr ModInt operator-(const ModInt& rhs) const noexcept {
return ModInt(*this) -= rhs;
}
constexpr ModInt operator*(const ModInt& rhs) const noexcept {
return ModInt(*this) *= rhs;
}
constexpr ModInt operator/(const ModInt& rhs) const noexcept {
return ModInt(*this) /= rhs;
}
constexpr bool operator==(const ModInt& rhs) const noexcept {
return _v == rhs._v;
}
constexpr bool operator!=(const ModInt& rhs) const noexcept {
return _v != rhs._v;
}
constexpr ModInt pow(long long n) const noexcept {
ModInt res = raw(1 % Modulus);
ModInt x = n < 0 ? inv() : *this;
uint64_t exponent = n < 0 ? uint64_t(-(n + 1)) + 1 : uint64_t(n);
while (exponent > 0) {
if (exponent & 1) res *= x;
x *= x;
exponent >>= 1;
}
return res;
}
constexpr ModInt inv() const noexcept {
int64_t a = _v, b = Modulus, u = 1, v = 0;
while (b) {
int64_t t = a / b;
a -= t * b;
std::swap(a, b);
u -= t * v;
std::swap(u, v);
}
assert(a == 1);
u %= Modulus;
if (u < 0) u += Modulus;
return raw(static_cast<uint32_t>(u));
}
friend std::ostream& operator<<(std::ostream& os, const ModInt& rhs) {
return os << rhs._v;
}
friend std::istream& operator>>(std::istream& is, ModInt& rhs) {
long long v;
is >> v;
rhs = ModInt(v);
return is;
}
};
using modint998244353 = ModInt<998244353>;
using modint1000000007 = ModInt<1000000007>;
template <int Id = 0>
struct DynamicModInt {
private:
uint32_t _v;
inline static uint32_t _mod = 1;
public:
static uint32_t mod() noexcept {
return _mod;
}
static void set_mod(uint32_t modulus) noexcept {
assert(modulus > 0);
assert(modulus <= uint32_t(1) << 31);
_mod = modulus;
}
static DynamicModInt raw(uint32_t v) noexcept {
assert(v < _mod);
DynamicModInt x;
x._v = v;
return x;
}
DynamicModInt() noexcept : _v(0) {}
template <class Integer, std::enable_if_t<std::is_integral_v<Integer>, int> = 0>
DynamicModInt(Integer v) noexcept {
if constexpr (std::is_signed_v<Integer>) {
int64_t x = static_cast<int64_t>(v) % static_cast<int64_t>(_mod);
if (x < 0) x += _mod;
_v = static_cast<uint32_t>(x);
} else {
_v = static_cast<uint32_t>(static_cast<uint64_t>(v) % _mod);
}
}
uint32_t val() const noexcept {
return _v;
}
DynamicModInt& operator++() noexcept {
_v++;
if (_v == _mod) _v = 0;
return *this;
}
DynamicModInt& operator--() noexcept {
if (_v == 0) _v = _mod;
_v--;
return *this;
}
DynamicModInt operator++(int) noexcept {
DynamicModInt result = *this;
++*this;
return result;
}
DynamicModInt operator--(int) noexcept {
DynamicModInt result = *this;
--*this;
return result;
}
DynamicModInt& operator+=(const DynamicModInt& rhs) noexcept {
_v += rhs._v;
if (_v >= _mod) _v -= _mod;
return *this;
}
DynamicModInt& operator-=(const DynamicModInt& rhs) noexcept {
_v -= rhs._v;
if (_v >= _mod) _v += _mod;
return *this;
}
DynamicModInt& operator*=(const DynamicModInt& rhs) noexcept {
_v = static_cast<uint32_t>(uint64_t(_v) * rhs._v % _mod);
return *this;
}
DynamicModInt& operator/=(const DynamicModInt& rhs) noexcept {
return *this *= rhs.inv();
}
DynamicModInt operator+(const DynamicModInt& rhs) const noexcept {
return DynamicModInt(*this) += rhs;
}
DynamicModInt operator-(const DynamicModInt& rhs) const noexcept {
return DynamicModInt(*this) -= rhs;
}
DynamicModInt operator*(const DynamicModInt& rhs) const noexcept {
return DynamicModInt(*this) *= rhs;
}
DynamicModInt operator/(const DynamicModInt& rhs) const noexcept {
return DynamicModInt(*this) /= rhs;
}
bool operator==(const DynamicModInt& rhs) const noexcept {
return _v == rhs._v;
}
bool operator!=(const DynamicModInt& rhs) const noexcept {
return _v != rhs._v;
}
DynamicModInt pow(long long exponent) const noexcept {
DynamicModInt result = raw(1 % _mod);
DynamicModInt base = exponent < 0 ? inv() : *this;
uint64_t magnitude =
exponent < 0 ? uint64_t(-(exponent + 1)) + 1 : uint64_t(exponent);
while (magnitude > 0) {
if (magnitude & 1) result *= base;
base *= base;
magnitude >>= 1;
}
return result;
}
DynamicModInt inv() const noexcept {
int64_t a = _v, b = _mod, u = 1, v = 0;
while (b) {
int64_t quotient = a / b;
a -= quotient * b;
std::swap(a, b);
u -= quotient * v;
std::swap(u, v);
}
assert(a == 1);
u %= _mod;
if (u < 0) u += _mod;
return raw(static_cast<uint32_t>(u));
}
friend std::ostream& operator<<(std::ostream& os, const DynamicModInt& rhs) {
return os << rhs._v;
}
friend std::istream& operator>>(std::istream& is, DynamicModInt& rhs) {
long long value;
is >> value;
rhs = DynamicModInt(value);
return is;
}
};
} // namespace math
} // namespace m1une
#line 29 "math/fps/convolution.hpp"
namespace m1une {
namespace fps {
namespace internal {
template <class Mint, class = void>
struct has_static_modulus : std::false_type {};
template <class Mint>
struct has_static_modulus<
Mint, std::void_t<decltype(std::integral_constant<uint32_t, Mint::mod()>{})>>
: std::true_type {};
constexpr uint32_t primitive_root_constexpr(uint32_t mod) {
if (mod == 2) return 1;
if (mod == 167772161) return 3;
if (mod == 469762049) return 3;
if (mod == 754974721) return 11;
if (mod == 998244353) return 3;
if (mod == 1224736769) return 3;
uint32_t divisors[32] = {};
int count = 0;
uint32_t x = mod - 1;
for (uint32_t p = 2; uint64_t(p) * p <= x; p++) {
if (x % p != 0) continue;
divisors[count++] = p;
while (x % p == 0) x /= p;
}
if (x > 1) divisors[count++] = x;
for (uint32_t g = 2;; g++) {
bool ok = true;
for (int i = 0; i < count; i++) {
uint64_t value = 1;
uint64_t base = g;
uint32_t exponent = (mod - 1) / divisors[i];
while (exponent > 0) {
if (exponent & 1) value = value * base % mod;
base = base * base % mod;
exponent >>= 1;
}
if (value == 1) {
ok = false;
break;
}
}
if (ok) return g;
}
}
constexpr int two_adic_order(uint32_t x) {
int result = 0;
while ((x & 1) == 0) {
x >>= 1;
result++;
}
return result;
}
template <class Mint>
struct NttRoots {
static constexpr int max_base = two_adic_order(Mint::mod() - 1);
std::array<Mint, max_base + 1> root;
std::array<Mint, max_base + 1> inverse_root;
std::array<Mint, max_base> rate;
std::array<Mint, max_base> inverse_rate;
std::array<Mint, max_base> rate_radix4;
std::array<Mint, max_base> inverse_rate_radix4;
NttRoots() {
constexpr uint32_t primitive_root = primitive_root_constexpr(Mint::mod());
for (int level = 1; level <= max_base; level++) {
root[level] = Mint(primitive_root).pow((Mint::mod() - 1) >> level);
inverse_root[level] = root[level].inv();
}
Mint product = 1;
Mint inverse_product = 1;
for (int i = 0; i + 1 < max_base; i++) {
rate[i] = root[i + 2] * product;
inverse_rate[i] = inverse_root[i + 2] * inverse_product;
product *= inverse_root[i + 2];
inverse_product *= root[i + 2];
}
product = 1;
inverse_product = 1;
for (int i = 0; i + 2 < max_base; i++) {
rate_radix4[i] = root[i + 3] * product;
inverse_rate_radix4[i] = inverse_root[i + 3] * inverse_product;
product *= inverse_root[i + 3];
inverse_product *= root[i + 3];
}
}
};
template <class Mint>
const NttRoots<Mint>& ntt_roots() {
static const NttRoots<Mint> roots;
return roots;
}
template <class Mint>
void ntt(std::vector<Mint>& a, bool inverse, bool normalize = true) {
const int n = int(a.size());
assert(n > 0 && (n & (n - 1)) == 0);
assert((Mint::mod() - 1) % uint32_t(n) == 0);
const auto& roots = ntt_roots<Mint>();
const int height = two_adic_order(uint32_t(n));
if (!inverse) {
int phase = 0;
while (phase < height) {
if (height - phase == 1) {
const int width = 1 << (height - phase - 1);
Mint twiddle = 1;
for (int block = 0; block < (1 << phase); block++) {
const int offset = block << (height - phase);
for (int i = 0; i < width; i++) {
const Mint left = a[offset + i];
const Mint right = a[offset + i + width] * twiddle;
a[offset + i] = left + right;
a[offset + i + width] = left - right;
}
if (block + 1 != (1 << phase))
twiddle *= roots.rate[__builtin_ctz(~uint32_t(block))];
}
phase++;
continue;
}
const int width = 1 << (height - phase - 2);
Mint twiddle = 1;
const Mint imaginary = roots.root[2];
for (int block = 0; block < (1 << phase); block++) {
const Mint twiddle2 = twiddle * twiddle;
const Mint twiddle3 = twiddle2 * twiddle;
const int offset = block << (height - phase);
for (int i = 0; i < width; i++) {
const uint64_t mod2 = uint64_t(Mint::mod()) * Mint::mod();
const uint64_t a0 = a[offset + i].val();
const uint64_t a1 = uint64_t(a[offset + i + width].val()) * twiddle.val();
const uint64_t a2 =
uint64_t(a[offset + i + 2 * width].val()) * twiddle2.val();
const uint64_t a3 =
uint64_t(a[offset + i + 3 * width].val()) * twiddle3.val();
const uint64_t a1na3i =
uint64_t(Mint(a1 + mod2 - a3).val()) * imaginary.val();
const uint64_t negative_a2 = mod2 - a2;
a[offset + i] = Mint(a0 + a2 + a1 + a3);
a[offset + i + width] = Mint(a0 + a2 + 2 * mod2 - a1 - a3);
a[offset + i + 2 * width] = Mint(a0 + negative_a2 + a1na3i);
a[offset + i + 3 * width] = Mint(a0 + negative_a2 + mod2 - a1na3i);
}
if (block + 1 != (1 << phase))
twiddle *= roots.rate_radix4[__builtin_ctz(~uint32_t(block))];
}
phase += 2;
}
} else {
int phase = height;
while (phase > 0) {
if (phase == 1) {
const int width = 1 << (height - phase);
Mint twiddle = 1;
for (int block = 0; block < (1 << (phase - 1)); block++) {
const int offset = block << (height - phase + 1);
for (int i = 0; i < width; i++) {
const Mint left = a[offset + i];
const Mint right = a[offset + i + width];
a[offset + i] = left + right;
a[offset + i + width] = (left - right) * twiddle;
}
if (block + 1 != (1 << (phase - 1)))
twiddle *= roots.inverse_rate[__builtin_ctz(~uint32_t(block))];
}
phase--;
continue;
}
const int width = 1 << (height - phase);
Mint twiddle = 1;
const Mint inverse_imaginary = roots.inverse_root[2];
for (int block = 0; block < (1 << (phase - 2)); block++) {
const Mint twiddle2 = twiddle * twiddle;
const Mint twiddle3 = twiddle2 * twiddle;
const int offset = block << (height - phase + 2);
for (int i = 0; i < width; i++) {
const uint64_t a0 = a[offset + i].val();
const uint64_t a1 = a[offset + i + width].val();
const uint64_t a2 = a[offset + i + 2 * width].val();
const uint64_t a3 = a[offset + i + 3 * width].val();
const uint64_t a2na3i =
uint64_t(Mint((Mint::mod() + a2 - a3) * inverse_imaginary.val()).val());
a[offset + i] = Mint(a0 + a1 + a2 + a3);
a[offset + i + width] =
Mint((a0 + Mint::mod() - a1 + a2na3i) * twiddle.val());
a[offset + i + 2 * width] = Mint(
(a0 + a1 + 2ULL * Mint::mod() - a2 - a3) * twiddle2.val());
a[offset + i + 3 * width] = Mint(
(a0 + Mint::mod() - a1 + Mint::mod() - a2na3i) * twiddle3.val());
}
if (block + 1 != (1 << (phase - 2)))
twiddle *= roots.inverse_rate_radix4[__builtin_ctz(~uint32_t(block))];
}
phase -= 2;
}
if (normalize) {
const Mint inverse_n = Mint(n).inv();
for (Mint& value : a) value *= inverse_n;
}
}
}
#ifdef M1UNE_FPS_HAS_X86_SIMD
#pragma GCC push_options
#pragma GCC target("avx2,bmi")
template <class Mint>
__attribute__((target("avx2,bmi"), hot))
std::vector<Mint> convolution_998244353_simd(const std::vector<Mint>& a,
const std::vector<Mint>& b) {
const int result_size = int(a.size() + b.size() - 1);
int n = 1;
while (n < result_size) n <<= 1;
const bool squaring = &a == &b;
auto* transformed_a = static_cast<uint32_t*>(
::operator new[](sizeof(uint32_t) * n, std::align_val_t(32)));
auto* transformed_b = squaring
? transformed_a
: static_cast<uint32_t*>(::operator new[](
sizeof(uint32_t) * n, std::align_val_t(32)));
if constexpr (std::is_same_v<Mint, math::ModInt<998244353>>) {
static_assert(sizeof(Mint) == sizeof(uint32_t) && std::is_trivially_copyable_v<Mint>);
std::memcpy(transformed_a, a.data(), sizeof(uint32_t) * a.size());
if (!squaring)
std::memcpy(transformed_b, b.data(), sizeof(uint32_t) * b.size());
} else {
for (int i = 0; i < int(a.size()); i++) transformed_a[i] = a[i].val();
if (!squaring)
for (int i = 0; i < int(b.size()); i++) transformed_b[i] = b[i].val();
}
std::memset(transformed_a + a.size(), 0, sizeof(uint32_t) * (n - a.size()));
if (!squaring)
std::memset(transformed_b + b.size(), 0, sizeof(uint32_t) * (n - b.size()));
static constexpr fast998_v2::FNTT32_info transform(998244353);
const std::size_t vector_size = std::size_t(n) >> 3;
fast998_v2::vector_dif(reinterpret_cast<__m256i*>(transformed_a), vector_size, &transform);
if (!squaring)
fast998_v2::vector_dif(reinterpret_cast<__m256i*>(transformed_b), vector_size,
&transform);
fast998_v2::vector_convolution_direct(
reinterpret_cast<__m256i*>(transformed_a),
reinterpret_cast<const __m256i*>(transformed_b), vector_size, &transform);
fast998_v2::vector_dit<true>(reinterpret_cast<__m256i*>(transformed_a), vector_size,
&transform);
std::vector<Mint> result(result_size);
for (int j = 0; j < result_size; j++) result[j] = Mint::raw(transformed_a[j]);
::operator delete[](transformed_a, std::align_val_t(32));
if (!squaring) ::operator delete[](transformed_b, std::align_val_t(32));
return result;
}
#pragma GCC pop_options
#endif
} // namespace internal
template <class Mint>
std::vector<Mint> convolution_naive(const std::vector<Mint>& a, const std::vector<Mint>& b) {
if (a.empty() || b.empty()) return {};
std::vector<Mint> result(a.size() + b.size() - 1);
if (a.size() < b.size()) {
for (int i = 0; i < int(a.size()); i++) {
for (int j = 0; j < int(b.size()); j++) result[i + j] += a[i] * b[j];
}
} else {
for (int j = 0; j < int(b.size()); j++) {
for (int i = 0; i < int(a.size()); i++) result[i + j] += a[i] * b[j];
}
}
return result;
}
template <class Mint>
std::vector<Mint> convolution_ntt(const std::vector<Mint>& a, const std::vector<Mint>& b) {
const int result_size = int(a.size() + b.size() - 1);
int n = 1;
while (n < result_size) n <<= 1;
assert((Mint::mod() - 1) % uint32_t(n) == 0);
#ifdef M1UNE_FPS_HAS_X86_SIMD
if constexpr (Mint::mod() == 998244353) {
if (n >= 64 && __builtin_cpu_supports("avx2"))
return internal::convolution_998244353_simd(a, b);
}
#endif
// Allocate the padded buffers directly. Constructing from the inputs and
// then resizing used to allocate and copy both large operands twice.
const bool squaring = &a == &b;
std::vector<Mint> fa(n);
std::copy(a.begin(), a.end(), fa.begin());
internal::ntt(fa, false);
const Mint inverse_n = Mint(n).inv();
if (squaring) {
for (int i = 0; i < n; i++) fa[i] *= fa[i] * inverse_n;
} else {
std::vector<Mint> fb(n);
std::copy(b.begin(), b.end(), fb.begin());
internal::ntt(fb, false);
for (int i = 0; i < n; i++) fa[i] *= fb[i] * inverse_n;
}
internal::ntt(fa, true, false);
fa.resize(result_size);
return fa;
}
namespace internal {
template <class Mint>
std::vector<Mint> convolution_998244353_blocked_scalar(const std::vector<Mint>& a,
const std::vector<Mint>& b,
int transform_size) {
assert(Mint::mod() == 998244353);
assert(transform_size >= 2 && (transform_size & (transform_size - 1)) == 0);
assert((Mint::mod() - 1) % uint32_t(transform_size) == 0);
const int block_size = transform_size / 2;
const int a_blocks = int((a.size() + block_size - 1) / block_size);
const int b_blocks = int((b.size() + block_size - 1) / block_size);
auto transform_blocks = [&](const std::vector<Mint>& values, int block_count) {
std::vector<std::vector<Mint>> blocks;
blocks.reserve(block_count);
for (int block = 0; block < block_count; block++) {
const int begin = block * block_size;
const int count = std::min(block_size, int(values.size()) - begin);
std::vector<Mint> transformed(transform_size);
std::copy_n(values.begin() + begin, count, transformed.begin());
ntt(transformed, false);
blocks.emplace_back(std::move(transformed));
}
return blocks;
};
std::vector<std::vector<Mint>> transformed_a = transform_blocks(a, a_blocks);
std::vector<std::vector<Mint>> transformed_b = transform_blocks(b, b_blocks);
const int result_size = int(a.size() + b.size() - 1);
std::vector<Mint> result(result_size);
std::vector<Mint> transformed_result(transform_size);
for (int diagonal = 0; diagonal < a_blocks + b_blocks - 1; diagonal++) {
std::fill(transformed_result.begin(), transformed_result.end(), Mint(0));
const int first_a = std::max(0, diagonal - (b_blocks - 1));
const int last_a = std::min(a_blocks - 1, diagonal);
for (int a_block = first_a; a_block <= last_a; a_block++) {
const int b_block = diagonal - a_block;
for (int i = 0; i < transform_size; i++)
transformed_result[i] +=
transformed_a[a_block][i] * transformed_b[b_block][i];
}
ntt(transformed_result, true);
const int output_offset = diagonal * block_size;
const int output_count = std::min(transform_size, result_size - output_offset);
for (int i = 0; i < output_count; i++)
result[output_offset + i] += transformed_result[i];
}
return result;
}
#ifdef M1UNE_FPS_HAS_X86_SIMD
class AlignedUint32Buffer {
private:
uint32_t* data_;
public:
explicit AlignedUint32Buffer(std::size_t size)
: data_(static_cast<uint32_t*>(
::operator new[](sizeof(uint32_t) * size, std::align_val_t(32)))) {}
AlignedUint32Buffer(const AlignedUint32Buffer&) = delete;
AlignedUint32Buffer& operator=(const AlignedUint32Buffer&) = delete;
AlignedUint32Buffer(AlignedUint32Buffer&& other) noexcept : data_(other.data_) {
other.data_ = nullptr;
}
AlignedUint32Buffer& operator=(AlignedUint32Buffer&& other) noexcept {
if (this == &other) return *this;
::operator delete[](data_, std::align_val_t(32));
data_ = other.data_;
other.data_ = nullptr;
return *this;
}
~AlignedUint32Buffer() {
::operator delete[](data_, std::align_val_t(32));
}
uint32_t* data() {
return data_;
}
const uint32_t* data() const {
return data_;
}
};
template <class Mint>
__attribute__((target("avx2,bmi"), hot))
std::vector<Mint> convolution_998244353_blocked_simd(const std::vector<Mint>& a,
const std::vector<Mint>& b,
int transform_size) {
assert(Mint::mod() == 998244353);
assert(transform_size >= 64 && (transform_size & (transform_size - 1)) == 0);
assert((Mint::mod() - 1) % uint32_t(transform_size) == 0);
const int block_size = transform_size / 2;
const int a_blocks = int((a.size() + block_size - 1) / block_size);
const int b_blocks = int((b.size() + block_size - 1) / block_size);
static constexpr fast998_v2::FNTT32_info transform(998244353);
const std::size_t vector_size = std::size_t(transform_size) / 8;
auto transform_blocks = [&](const std::vector<Mint>& values, int block_count) {
std::vector<AlignedUint32Buffer> blocks;
blocks.reserve(block_count);
for (int block = 0; block < block_count; block++) {
const int begin = block * block_size;
const int count = std::min(block_size, int(values.size()) - begin);
AlignedUint32Buffer transformed(transform_size);
if constexpr (std::is_same_v<Mint, math::ModInt<998244353>>) {
static_assert(sizeof(Mint) == sizeof(uint32_t) &&
std::is_trivially_copyable_v<Mint>);
std::memcpy(transformed.data(), values.data() + begin,
sizeof(uint32_t) * count);
} else {
for (int i = 0; i < count; i++)
transformed.data()[i] = values[begin + i].val();
}
std::memset(transformed.data() + count, 0,
sizeof(uint32_t) * (transform_size - count));
fast998_v2::vector_dif(reinterpret_cast<__m256i*>(transformed.data()),
vector_size, &transform);
blocks.emplace_back(std::move(transformed));
}
return blocks;
};
std::vector<AlignedUint32Buffer> transformed_a = transform_blocks(a, a_blocks);
std::vector<AlignedUint32Buffer> transformed_b = transform_blocks(b, b_blocks);
const int result_size = int(a.size() + b.size() - 1);
std::vector<Mint> result(result_size);
AlignedUint32Buffer transformed_result(transform_size);
for (int diagonal = 0; diagonal < a_blocks + b_blocks - 1; diagonal++) {
std::memset(transformed_result.data(), 0, sizeof(uint32_t) * transform_size);
const int first_a = std::max(0, diagonal - (b_blocks - 1));
const int last_a = std::min(a_blocks - 1, diagonal);
for (int a_block = first_a; a_block <= last_a; a_block++) {
const int b_block = diagonal - a_block;
fast998_v2::vector_convolution_accumulate(
reinterpret_cast<__m256i*>(transformed_result.data()),
reinterpret_cast<const __m256i*>(transformed_a[a_block].data()),
reinterpret_cast<const __m256i*>(transformed_b[b_block].data()),
vector_size, &transform);
}
fast998_v2::vector_dit<true>(
reinterpret_cast<__m256i*>(transformed_result.data()), vector_size,
&transform);
const int output_offset = diagonal * block_size;
const int output_count = std::min(transform_size, result_size - output_offset);
for (int i = 0; i < output_count; i++) {
uint32_t value = result[output_offset + i].val() + transformed_result.data()[i];
if (value >= Mint::mod()) value -= Mint::mod();
result[output_offset + i] = Mint::raw(value);
}
}
return result;
}
#endif
template <class Mint>
std::vector<Mint> convolution_998244353_blocked(const std::vector<Mint>& a,
const std::vector<Mint>& b,
int transform_size = 1 << 23) {
#ifdef M1UNE_FPS_HAS_X86_SIMD
if (transform_size >= 64 && __builtin_cpu_supports("avx2"))
return convolution_998244353_blocked_simd(a, b, transform_size);
#endif
return convolution_998244353_blocked_scalar(a, b, transform_size);
}
} // namespace internal
template <class Mint>
std::vector<Mint> convolution(const std::vector<Mint>& a, const std::vector<Mint>& b) {
if (a.empty() || b.empty()) return {};
if (std::min(a.size(), b.size()) <= 32) return convolution_naive(a, b);
const int result_size = int(a.size() + b.size() - 1);
int n = 1;
while (n < result_size) n <<= 1;
if constexpr (internal::has_static_modulus<Mint>::value) {
if constexpr (Mint::mod() == 998244353) {
if (n > (1 << 23))
return internal::convolution_998244353_blocked(a, b);
}
if ((Mint::mod() - 1) % uint32_t(n) == 0) return convolution_ntt(a, b);
}
using Mint1 = math::ModInt<167772161>;
using Mint2 = math::ModInt<469762049>;
using Mint3 = math::ModInt<754974721>;
assert(n <= (1 << 24));
[[maybe_unused]] const unsigned __int128 coefficient_bound =
static_cast<unsigned __int128>(std::min(a.size(), b.size())) * (Mint::mod() - 1) *
(Mint::mod() - 1);
[[maybe_unused]] const unsigned __int128 crt_modulus =
static_cast<unsigned __int128>(Mint1::mod()) * Mint2::mod() * Mint3::mod();
assert(coefficient_bound < crt_modulus);
auto converted_convolution = [&]<class OtherMint>() {
std::vector<OtherMint> converted_a(a.size());
std::vector<OtherMint> converted_b(b.size());
for (int i = 0; i < int(a.size()); i++) converted_a[i] = OtherMint(a[i].val());
for (int i = 0; i < int(b.size()); i++) converted_b[i] = OtherMint(b[i].val());
return convolution_ntt(converted_a, converted_b);
};
std::vector<Mint1> c1 = converted_convolution.template operator()<Mint1>();
std::vector<Mint2> c2 = converted_convolution.template operator()<Mint2>();
std::vector<Mint3> c3 = converted_convolution.template operator()<Mint3>();
static const uint64_t inverse_mod1_mod2 = Mint2(Mint1::mod()).inv().val();
static const uint64_t mod1_mod3 = Mint1::mod() % Mint3::mod();
static const uint64_t mod1_mod2_mod3 =
mod1_mod3 * (Mint2::mod() % Mint3::mod()) % Mint3::mod();
static const uint64_t inverse_mod1_mod2_mod3 = Mint3(uint32_t(mod1_mod2_mod3)).inv().val();
const uint64_t target_mod = Mint::mod();
const uint64_t mod1_target = Mint1::mod() % target_mod;
const uint64_t mod1_mod2_target = mod1_target * (Mint2::mod() % target_mod) % target_mod;
std::vector<Mint> result(result_size);
for (int i = 0; i < result_size; i++) {
const uint64_t r1 = c1[i].val();
const uint64_t r2 = c2[i].val();
const uint64_t r3 = c3[i].val();
const uint64_t first =
(r2 + Mint2::mod() - r1 % Mint2::mod()) % Mint2::mod() * inverse_mod1_mod2 %
Mint2::mod();
const uint64_t combined_mod3 =
(r1 % Mint3::mod() + mod1_mod3 * (first % Mint3::mod())) % Mint3::mod();
const uint64_t second =
(r3 + Mint3::mod() - combined_mod3) % Mint3::mod() * inverse_mod1_mod2_mod3 %
Mint3::mod();
uint64_t value = r1 % target_mod;
value = (value + mod1_target * (first % target_mod)) % target_mod;
value = (value + mod1_mod2_target * (second % target_mod)) % target_mod;
result[i] = Mint::raw(uint32_t(value));
}
return result;
}
} // namespace fps
} // namespace m1une
#ifdef M1UNE_FPS_HAS_X86_SIMD
#undef M1UNE_FPS_HAS_X86_SIMD
#endif
#line 1 "math/fps/formal_power_series.hpp"
#line 10 "math/fps/formal_power_series.hpp"
#line 1 "math/modular_square_root.hpp"
#line 7 "math/modular_square_root.hpp"
namespace m1une {
namespace math {
namespace internal {
inline uint64_t modular_square_root_multiply(uint64_t lhs, uint64_t rhs, uint64_t mod) {
return static_cast<uint64_t>(static_cast<unsigned __int128>(lhs) * rhs % mod);
}
inline uint64_t modular_square_root_power(uint64_t base, uint64_t exponent, uint64_t mod) {
uint64_t result = 1 % mod;
while (exponent > 0) {
if (exponent & 1) result = modular_square_root_multiply(result, base, mod);
base = modular_square_root_multiply(base, base, mod);
exponent >>= 1;
}
return result;
}
} // namespace internal
// Returns x such that x * x = value (mod prime), or nullopt when no such x exists.
// The modulus must be prime.
inline std::optional<uint64_t> modular_square_root(uint64_t value, uint64_t prime) {
assert(prime >= 2);
value %= prime;
if (value == 0 || prime == 2) return value;
if (internal::modular_square_root_power(value, (prime - 1) / 2, prime) != 1) {
return std::nullopt;
}
if (prime % 4 == 3) {
return internal::modular_square_root_power(value, prime / 4 + 1, prime);
}
uint64_t odd_part = prime - 1;
int power_of_two = 0;
while ((odd_part & 1) == 0) {
odd_part >>= 1;
power_of_two++;
}
uint64_t non_residue = 2;
while (internal::modular_square_root_power(non_residue, (prime - 1) / 2, prime) == 1) {
non_residue++;
}
uint64_t c = internal::modular_square_root_power(non_residue, odd_part, prime);
uint64_t root = internal::modular_square_root_power(value, odd_part / 2 + 1, prime);
uint64_t remainder = internal::modular_square_root_power(value, odd_part, prime);
int remaining_power = power_of_two;
while (remainder != 1) {
int exponent = 1;
uint64_t squared = internal::modular_square_root_multiply(remainder, remainder, prime);
while (squared != 1) {
squared = internal::modular_square_root_multiply(squared, squared, prime);
exponent++;
}
uint64_t correction = c;
for (int i = 0; i < remaining_power - exponent - 1; i++) {
correction = internal::modular_square_root_multiply(correction, correction, prime);
}
root = internal::modular_square_root_multiply(root, correction, prime);
c = internal::modular_square_root_multiply(correction, correction, prime);
remainder = internal::modular_square_root_multiply(remainder, c, prime);
remaining_power = exponent;
}
return root;
}
template <class Mint>
std::optional<Mint> modular_square_root(Mint value) {
auto root = modular_square_root(static_cast<uint64_t>(value.val()),
static_cast<uint64_t>(Mint::mod()));
if (!root.has_value()) return std::nullopt;
return Mint(*root);
}
} // namespace math
} // namespace m1une
#line 13 "math/fps/formal_power_series.hpp"
namespace m1une {
namespace fps {
template <class Mint>
struct FormalPowerSeries : std::vector<Mint> {
using std::vector<Mint>::vector;
using Fps = FormalPowerSeries;
FormalPowerSeries() = default;
FormalPowerSeries(const std::vector<Mint>& values) : std::vector<Mint>(values) {}
FormalPowerSeries(std::vector<Mint>&& values) : std::vector<Mint>(std::move(values)) {}
Fps& shrink() {
while (!this->empty() && this->back() == Mint(0)) this->pop_back();
return *this;
}
Fps pre(int degree) const {
assert(degree >= 0);
Fps result(this->begin(), this->begin() + std::min<int>(degree, this->size()));
result.resize(degree);
return result;
}
Fps reversed(int size = -1) const {
Fps result = *this;
if (size >= 0) result.resize(size);
std::reverse(result.begin(), result.end());
return result;
}
Fps& operator+=(const Fps& rhs) {
if (this->size() < rhs.size()) this->resize(rhs.size());
for (int i = 0; i < int(rhs.size()); i++) (*this)[i] += rhs[i];
return *this;
}
Fps& operator-=(const Fps& rhs) {
if (this->size() < rhs.size()) this->resize(rhs.size());
for (int i = 0; i < int(rhs.size()); i++) (*this)[i] -= rhs[i];
return *this;
}
Fps& operator*=(const Fps& rhs) {
std::vector<Mint> lhs(this->begin(), this->end());
*this = convolution(lhs, rhs);
return *this;
}
Fps& operator*=(Mint rhs) {
for (Mint& value : *this) value *= rhs;
return *this;
}
Fps& operator/=(Mint rhs) {
return *this *= rhs.inv();
}
Fps& operator<<=(int shift) {
assert(shift >= 0);
this->insert(this->begin(), shift, Mint(0));
return *this;
}
Fps& operator>>=(int shift) {
assert(shift >= 0);
if (shift >= int(this->size())) {
this->clear();
} else {
this->erase(this->begin(), this->begin() + shift);
}
return *this;
}
Fps operator+() const {
return *this;
}
Fps operator-() const {
Fps result = *this;
for (Mint& value : result) value = Mint(0) - value;
return result;
}
friend Fps operator+(Fps lhs, const Fps& rhs) {
return lhs += rhs;
}
friend Fps operator-(Fps lhs, const Fps& rhs) {
return lhs -= rhs;
}
friend Fps operator*(Fps lhs, const Fps& rhs) {
return lhs *= rhs;
}
friend Fps operator*(Fps lhs, Mint rhs) {
return lhs *= rhs;
}
friend Fps operator*(Mint lhs, Fps rhs) {
return rhs *= lhs;
}
friend Fps operator/(Fps lhs, Mint rhs) {
return lhs /= rhs;
}
friend Fps operator<<(Fps lhs, int shift) {
return lhs <<= shift;
}
friend Fps operator>>(Fps lhs, int shift) {
return lhs >>= shift;
}
Fps derivative() const {
if (this->empty()) return {};
Fps result(this->size() - 1);
for (int i = 1; i < int(this->size()); i++) result[i - 1] = (*this)[i] * Mint(i);
return result;
}
Fps integral() const {
Fps result(this->size() + 1);
if (this->empty()) return result;
assert(this->size() < Mint::mod());
std::vector<Mint> inverse(this->size() + 1);
inverse[1] = 1;
for (int i = 2; i <= int(this->size()); i++) {
inverse[i] = Mint(0) - Mint(Mint::mod() / uint32_t(i)) * inverse[Mint::mod() % uint32_t(i)];
}
for (int i = 0; i < int(this->size()); i++) result[i + 1] = (*this)[i] * inverse[i + 1];
return result;
}
Mint evaluate(Mint x) const {
Mint result = 0;
for (auto it = this->rbegin(); it != this->rend(); ++it) result = result * x + *it;
return result;
}
Fps inv(int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(degree >= 0);
if (degree == 0) return {};
assert(!this->empty() && (*this)[0] != Mint(0));
Fps result(1, (*this)[0].inv());
for (int size = 1; size < degree; size <<= 1) {
const int next_size = std::min(size << 1, degree);
const int transform_size = size << 1;
if (size >= 32 && (Mint::mod() - 1) % uint32_t(transform_size) == 0) {
// Newton's g <- g(2-fg), restricted to the newly determined
// half. Keeping g in the frequency domain avoids two general
// convolutions and their 2x larger padding.
std::vector<Mint> transformed_f(transform_size);
std::copy_n(this->begin(), std::min<int>(this->size(), next_size),
transformed_f.begin());
std::vector<Mint> transformed_g(transform_size);
std::copy(result.begin(), result.end(), transformed_g.begin());
internal::ntt(transformed_f, false);
internal::ntt(transformed_g, false);
std::vector<Mint> error(transform_size);
for (int i = 0; i < transform_size; i++)
error[i] = transformed_f[i] * transformed_g[i];
internal::ntt(error, true);
std::fill(error.begin(), error.begin() + size, Mint(0));
internal::ntt(error, false);
for (int i = 0; i < transform_size; i++) error[i] *= transformed_g[i];
internal::ntt(error, true);
result.resize(next_size);
for (int i = size; i < next_size; i++) result[i] = Mint(0) - error[i];
continue;
}
Fps product = this->pre(next_size) * result;
product.resize(next_size);
for (Mint& value : product) value = Mint(0) - value;
product[0] += Mint(2);
result = (result * product).pre(next_size);
}
return result.pre(degree);
}
Fps log(int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(degree >= 0);
if (degree == 0) return {};
assert(!this->empty() && (*this)[0] == Mint(1));
return (derivative() * inv(degree)).pre(degree - 1).integral();
}
Fps exp(int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(degree >= 0);
if (degree == 0) return {};
assert(this->empty() || (*this)[0] == Mint(0));
Fps result(1, Mint(1));
for (int size = 1; size < degree; size <<= 1) {
const int next_size = std::min(size << 1, degree);
Fps correction = this->pre(next_size) - result.log(next_size);
correction[0] += Mint(1);
result = (result * correction).pre(next_size);
}
return result.pre(degree);
}
Fps pow(long long exponent, int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(exponent >= 0 && degree >= 0);
if (degree == 0) return {};
if (exponent == 0) {
Fps result(degree);
result[0] = 1;
return result;
}
int first = 0;
while (first < int(this->size()) && (*this)[first] == Mint(0)) first++;
if (first == int(this->size()) || first > (degree - 1) / exponent) return Fps(degree);
const int shift = int(first * exponent);
const Mint leading = (*this)[first];
Fps normalized = (*this >> first) / leading;
Fps result = (normalized.log(degree - shift) * Mint(exponent)).exp(degree - shift);
result *= leading.pow(exponent);
result <<= shift;
result.resize(degree);
return result;
}
std::optional<Fps> sqrt(int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(degree >= 0);
if (degree == 0) return Fps();
int first = 0;
while (first < int(this->size()) && (*this)[first] == Mint(0)) first++;
if (first == int(this->size())) return Fps(degree);
if (first >= degree) return Fps(degree);
if (first & 1) return std::nullopt;
const int shift = first / 2;
auto leading_root = m1une::math::modular_square_root((*this)[first]);
if (!leading_root.has_value()) return std::nullopt;
const int result_degree = degree - shift;
Fps normalized = (*this >> first) / (*this)[first];
Fps result = (normalized.log(result_degree) / Mint(2)).exp(result_degree);
result *= *leading_root;
result <<= shift;
result.resize(degree);
return result;
}
std::pair<Fps, Fps> divmod(const Fps& divisor) const {
Fps dividend = *this;
Fps normalized_divisor = divisor;
dividend.shrink();
normalized_divisor.shrink();
assert(!normalized_divisor.empty());
if (dividend.size() < normalized_divisor.size()) return std::make_pair(Fps(), dividend);
const int quotient_size = int(dividend.size() - normalized_divisor.size() + 1);
Fps quotient =
(dividend.reversed().pre(quotient_size) * normalized_divisor.reversed().inv(quotient_size))
.pre(quotient_size)
.reversed();
quotient.shrink();
Fps remainder = dividend - normalized_divisor * quotient;
remainder.resize(normalized_divisor.size() - 1);
remainder.shrink();
return std::make_pair(std::move(quotient), std::move(remainder));
}
Fps& operator/=(const Fps& rhs) {
*this = divmod(rhs).first;
return *this;
}
Fps& operator%=(const Fps& rhs) {
*this = divmod(rhs).second;
return *this;
}
friend Fps operator/(Fps lhs, const Fps& rhs) {
return lhs /= rhs;
}
friend Fps operator%(Fps lhs, const Fps& rhs) {
return lhs %= rhs;
}
Fps taylor_shift(Mint shift) const {
const int n = int(this->size());
if (n == 0) return {};
assert(uint32_t(n) < Mint::mod());
std::vector<Mint> factorial(n, Mint(1));
std::vector<Mint> inverse_factorial(n, Mint(1));
for (int i = 1; i < n; i++) factorial[i] = factorial[i - 1] * Mint(i);
inverse_factorial[n - 1] = factorial[n - 1].inv();
for (int i = n - 1; i > 0; i--) inverse_factorial[i - 1] = inverse_factorial[i] * Mint(i);
Fps left(n);
Fps right(n);
Mint power = 1;
for (int i = 0; i < n; i++) {
left[n - 1 - i] = (*this)[i] * factorial[i];
right[i] = power * inverse_factorial[i];
power *= shift;
}
Fps product = left * right;
Fps result(n);
for (int i = 0; i < n; i++) result[i] = product[n - 1 - i] * inverse_factorial[i];
return result;
}
};
} // namespace fps
} // namespace m1une
#line 1 "math/combinatorics.hpp"
#line 7 "math/combinatorics.hpp"
namespace m1une {
namespace math {
template <class Mint>
struct Combinatorics {
private:
std::vector<Mint> _factorial;
std::vector<Mint> _inverse_factorial;
public:
explicit Combinatorics(int maximum = 0) : _factorial(1, Mint(1)), _inverse_factorial(1, Mint(1)) {
ensure(maximum);
}
int maximum() const {
return int(_factorial.size()) - 1;
}
void ensure(int maximum) {
assert(maximum >= 0);
assert(static_cast<uint64_t>(maximum) < Mint::mod());
if (maximum <= this->maximum()) return;
const int old_maximum = this->maximum();
_factorial.resize(maximum + 1);
_inverse_factorial.resize(maximum + 1);
for (int i = old_maximum + 1; i <= maximum; i++) {
_factorial[i] = _factorial[i - 1] * Mint(i);
}
_inverse_factorial[maximum] = _factorial[maximum].inv();
for (int i = maximum; i > old_maximum; i--) {
_inverse_factorial[i - 1] = _inverse_factorial[i] * Mint(i);
}
}
Mint factorial(int n) const {
assert(0 <= n && n <= maximum());
return _factorial[n];
}
Mint inverse_factorial(int n) const {
assert(0 <= n && n <= maximum());
return _inverse_factorial[n];
}
Mint inverse(int n) const {
assert(1 <= n && n <= maximum());
return _factorial[n - 1] * _inverse_factorial[n];
}
Mint binom(int n, int k) const {
if (k < 0 || k > n) return Mint(0);
assert(n <= maximum());
return _factorial[n] * _inverse_factorial[k] * _inverse_factorial[n - k];
}
Mint perm(int n, int k) const {
if (k < 0 || k > n) return Mint(0);
assert(n <= maximum());
return _factorial[n] * _inverse_factorial[n - k];
}
Mint multiset(int types, int count) const {
if (types < 0 || count < 0) return Mint(0);
if (types == 0) return Mint(count == 0);
const long long total = static_cast<long long>(types) + count - 1;
assert(total <= maximum());
return binom(static_cast<int>(total), count);
}
Mint catalan(int n) const {
assert(n >= 0);
const long long doubled = 2LL * n;
assert(doubled <= maximum());
return binom(int(doubled), n) - binom(int(doubled), n + 1);
}
};
} // namespace math
} // namespace m1une
#line 13 "graph/counting.hpp"
namespace m1une {
namespace graph {
namespace graph_counting_detail {
template <class Mint>
using Fps = fps::FormalPowerSeries<Mint>;
template <class Mint>
void assert_maximum(int maximum) {
assert(maximum >= 0);
assert(static_cast<uint64_t>(maximum) < Mint::mod());
}
template <class Mint>
Mint inverse_two() {
assert(Mint::mod() != 2);
return Mint(2).inv();
}
template <class Mint>
std::vector<Mint> to_egf(
std::vector<Mint> values,
const math::Combinatorics<Mint>& combinations
) {
for (int i = 0; i < int(values.size()); i++) {
values[i] *= combinations.inverse_factorial(i);
}
return values;
}
template <class Mint>
std::vector<Mint> from_egf(
std::vector<Mint> coefficients,
const math::Combinatorics<Mint>& combinations
) {
for (int i = 0; i < int(coefficients.size()); i++) {
coefficients[i] *= combinations.factorial(i);
}
return coefficients;
}
template <class Mint>
std::vector<Mint> two_to_binom2(int maximum) {
std::vector<Mint> result(maximum + 1);
result[0] = 1;
Mint multiplier = 1;
for (int n = 1; n <= maximum; n++) {
result[n] = result[n - 1] * multiplier;
multiplier += multiplier;
}
return result;
}
template <class Mint>
Fps<Mint> colored_bipartite_egf(
int maximum,
const math::Combinatorics<Mint>& combinations
) {
const Mint half = inverse_two<Mint>();
Fps<Mint> kernel(maximum + 1);
kernel[0] = 1;
Mint multiplier = 1;
for (int i = 1; i <= maximum; i++) {
kernel[i] = kernel[i - 1] * multiplier;
multiplier *= half;
}
for (int i = 0; i <= maximum; i++) {
kernel[i] *= combinations.inverse_factorial(i);
}
Fps<Mint> result = (kernel * kernel).pre(maximum + 1);
std::vector<Mint> edge_powers = two_to_binom2<Mint>(maximum);
for (int i = 0; i <= maximum; i++) result[i] *= edge_powers[i];
return result;
}
} // namespace graph_counting_detail
template <class Mint>
std::vector<Mint> count_labeled_undirected_graphs(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
return graph_counting_detail::two_to_binom2<Mint>(maximum);
}
template <class Mint>
std::vector<Mint> count_labeled_connected_graphs(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
graph_counting_detail::Fps<Mint> egf =
graph_counting_detail::to_egf(count_labeled_undirected_graphs<Mint>(maximum), combinations);
egf = egf.log(maximum + 1);
return graph_counting_detail::from_egf(std::move(egf), combinations);
}
template <class Mint>
std::vector<Mint> count_labeled_trees(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
std::vector<Mint> result(maximum + 1);
for (int n = 1; n <= maximum; n++) {
result[n] = (n == 1 ? Mint(1) : Mint(n).pow(n - 2));
}
return result;
}
template <class Mint>
std::vector<Mint> count_labeled_forests(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
graph_counting_detail::Fps<Mint> egf =
graph_counting_detail::to_egf(count_labeled_trees<Mint>(maximum), combinations);
egf = egf.exp(maximum + 1);
return graph_counting_detail::from_egf(std::move(egf), combinations);
}
template <class Mint>
std::vector<Mint> count_labeled_unicyclic_connected_graphs(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
using Fps = graph_counting_detail::Fps<Mint>;
Fps rooted_tree_egf(maximum + 1);
for (int n = 1; n <= maximum; n++) {
rooted_tree_egf[n] =
Mint(n).pow(n - 1) * combinations.inverse_factorial(n);
}
Fps one_minus_rooted(maximum + 1);
one_minus_rooted[0] = 1;
for (int i = 1; i <= maximum; i++) {
one_minus_rooted[i] = Mint(0) - rooted_tree_egf[i];
}
Fps egf = one_minus_rooted.log(maximum + 1);
for (Mint& coefficient : egf) coefficient = Mint(0) - coefficient;
egf -= rooted_tree_egf;
egf -= ((rooted_tree_egf * rooted_tree_egf).pre(maximum + 1) *
graph_counting_detail::inverse_two<Mint>());
egf *= graph_counting_detail::inverse_two<Mint>();
return graph_counting_detail::from_egf(std::move(egf), combinations);
}
template <class Mint>
std::vector<Mint> count_labeled_connected_eulerian_graphs(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
std::vector<Mint> all_even(maximum + 1);
all_even[0] = 1;
if (maximum >= 1) {
std::vector<Mint> shifted = count_labeled_undirected_graphs<Mint>(maximum - 1);
for (int n = 1; n <= maximum; n++) all_even[n] = shifted[n - 1];
}
graph_counting_detail::Fps<Mint> egf =
graph_counting_detail::to_egf(std::move(all_even), combinations);
egf = egf.log(maximum + 1);
return graph_counting_detail::from_egf(std::move(egf), combinations);
}
template <class Mint>
std::vector<Mint> count_labeled_connected_bipartite_graphs(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
graph_counting_detail::Fps<Mint> egf =
graph_counting_detail::colored_bipartite_egf(maximum, combinations).log(maximum + 1);
egf *= graph_counting_detail::inverse_two<Mint>();
return graph_counting_detail::from_egf(std::move(egf), combinations);
}
template <class Mint>
std::vector<Mint> count_labeled_bipartite_graphs(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
std::optional<graph_counting_detail::Fps<Mint>> egf =
graph_counting_detail::colored_bipartite_egf(maximum, combinations).sqrt(maximum + 1);
assert(egf.has_value());
return graph_counting_detail::from_egf(std::move(*egf), combinations);
}
template <class Mint>
std::vector<Mint> count_labeled_directed_graphs(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
std::vector<Mint> result(maximum + 1);
result[0] = 1;
Mint multiplier = 1;
const Mint four = 4;
for (int n = 1; n <= maximum; n++) {
result[n] = result[n - 1] * multiplier;
multiplier *= four;
}
return result;
}
template <class Mint>
std::vector<Mint> count_labeled_dags(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
using Fps = graph_counting_detail::Fps<Mint>;
Fps denominator(maximum + 1);
Mint multiplier = 1;
const Mint half = graph_counting_detail::inverse_two<Mint>();
denominator[0] = 1;
for (int n = 1; n <= maximum; n++) {
denominator[n] = denominator[n - 1] * multiplier;
multiplier *= half;
}
for (int n = 0; n <= maximum; n++) {
denominator[n] *= combinations.inverse_factorial(n);
if (n & 1) denominator[n] = Mint(0) - denominator[n];
}
Fps egf = denominator.inv(maximum + 1);
std::vector<Mint> edge_powers = graph_counting_detail::two_to_binom2<Mint>(maximum);
for (int n = 0; n <= maximum; n++) {
egf[n] *= combinations.factorial(n) * edge_powers[n];
}
return egf;
}
template <class Mint>
std::vector<Mint> count_labeled_strongly_connected_digraphs(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
graph_counting_detail::Fps<Mint> egf(maximum + 1);
std::vector<Mint> edge_powers = graph_counting_detail::two_to_binom2<Mint>(maximum);
for (int n = 0; n <= maximum; n++) {
egf[n] = edge_powers[n] * combinations.inverse_factorial(n);
}
egf = egf.inv(maximum + 1);
for (int n = 0; n <= maximum; n++) egf[n] *= edge_powers[n];
egf = egf.log(maximum + 1);
for (int n = 0; n <= maximum; n++) {
egf[n] = Mint(0) - egf[n] * combinations.factorial(n);
}
return egf;
}
template <class Mint>
std::vector<Mint> count_labeled_tournaments(int maximum) {
return count_labeled_undirected_graphs<Mint>(maximum);
}
template <class Mint>
std::vector<Mint> count_labeled_strongly_connected_tournaments(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
math::Combinatorics<Mint> combinations(maximum);
graph_counting_detail::Fps<Mint> egf =
graph_counting_detail::to_egf(count_labeled_tournaments<Mint>(maximum), combinations);
egf = egf.inv(maximum + 1);
if (!egf.empty()) egf[0] = 0;
for (int n = 0; n <= maximum; n++) {
egf[n] = Mint(0) - egf[n] * combinations.factorial(n);
}
return egf;
}
template <class Mint>
std::vector<Mint> count_unlabeled_rooted_trees(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
std::vector<Mint> result(maximum + 1);
if (maximum == 0) return result;
std::vector<Mint> divisor_sum(maximum + 1);
result[1] = 1;
for (int multiple = 1; multiple <= maximum; multiple++) {
divisor_sum[multiple] += result[1];
}
for (int n = 1; n < maximum; n++) {
Mint sum = 0;
for (int i = 1; i <= n; i++) {
sum += divisor_sum[i] * result[n - i + 1];
}
result[n + 1] = sum / Mint(n);
const int size = n + 1;
const Mint contribution = Mint(size) * result[size];
for (int multiple = size; multiple <= maximum; multiple += size) {
divisor_sum[multiple] += contribution;
}
}
return result;
}
template <class Mint>
std::vector<Mint> count_unlabeled_trees(int maximum) {
graph_counting_detail::assert_maximum<Mint>(maximum);
using Fps = graph_counting_detail::Fps<Mint>;
Fps rooted = count_unlabeled_rooted_trees<Mint>(maximum);
Fps rooted_square = (rooted * rooted).pre(maximum + 1);
const Mint half = graph_counting_detail::inverse_two<Mint>();
std::vector<Mint> result(maximum + 1);
for (int n = 1; n <= maximum; n++) {
result[n] = rooted[n] - rooted_square[n] * half;
if ((n & 1) == 0) result[n] += rooted[n / 2] * half;
}
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/directed.hpp"
#line 1 "graph/cycle_detection.hpp"
#line 5 "graph/cycle_detection.hpp"
#include <cstddef>
#line 7 "graph/cycle_detection.hpp"
#line 1 "graph/graph.hpp"
#line 8 "graph/graph.hpp"
namespace m1une {
namespace graph {
template <class T = int>
struct Edge {
using cost_type = T;
int from;
int to;
T cost;
int id;
bool alive;
Edge() : from(-1), to(-1), cost(T()), id(-1), alive(true) {}
Edge(int from_, int to_, T cost_ = T(1), int id_ = -1, bool alive_ = true)
: from(from_), to(to_), cost(cost_), id(id_), alive(alive_) {}
int other(int v) const {
assert(v == from || v == to);
return from ^ to ^ v;
}
};
template <class T = int>
struct Graph {
using edge_type = Edge<T>;
using cost_type = T;
private:
struct EdgePositions {
std::array<std::pair<int, int>, 2> value{};
int size = 0;
void push_back(std::pair<int, int> position) {
assert(size < 2);
value[size++] = position;
}
};
int _n;
int _edge_count;
std::vector<std::vector<edge_type>> _g;
std::vector<EdgePositions> _edge_positions;
public:
Graph() : _n(0), _edge_count(0) {}
explicit Graph(int n) : _n(n), _edge_count(0), _g(n) {
assert(0 <= n);
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
int edge_count() const {
return _edge_count;
}
int add_vertex() {
_g.emplace_back();
return _n++;
}
int add_directed_edge(int from, int to, T cost = T(1)) {
assert(0 <= from && from < _n);
assert(0 <= to && to < _n);
int id = _edge_count++;
int idx = int(_g[from].size());
_g[from].push_back(edge_type(from, to, cost, id));
_edge_positions.emplace_back();
_edge_positions.back().push_back({from, idx});
return id;
}
int add_edge(int u, int v, T cost = T(1)) {
assert(0 <= u && u < _n);
assert(0 <= v && v < _n);
int id = _edge_count++;
int u_idx = int(_g[u].size());
_g[u].push_back(edge_type(u, v, cost, id));
int v_idx = int(_g[v].size());
_g[v].push_back(edge_type(v, u, cost, id));
_edge_positions.emplace_back();
_edge_positions.back().push_back({u, u_idx});
_edge_positions.back().push_back({v, v_idx});
return id;
}
void set_edge_alive(int id, bool alive) {
assert(0 <= id && id < _edge_count);
for (int i = 0; i < _edge_positions[id].size; ++i) {
auto [v, idx] = _edge_positions[id].value[i];
_g[v][idx].alive = alive;
}
}
void erase_edge(int id) {
set_edge_alive(id, false);
}
void revive_edge(int id) {
set_edge_alive(id, true);
}
bool is_edge_alive(int id) const {
assert(0 <= id && id < _edge_count);
assert(_edge_positions[id].size != 0);
auto [v, idx] = _edge_positions[id].value[0];
return _g[v][idx].alive;
}
const std::vector<edge_type>& operator[](int v) const {
assert(0 <= v && v < _n);
return _g[v];
}
std::vector<edge_type>& operator[](int v) {
assert(0 <= v && v < _n);
return _g[v];
}
const std::vector<std::vector<edge_type>>& adjacency() const {
return _g;
}
std::vector<std::vector<edge_type>>& adjacency() {
return _g;
}
std::vector<edge_type> edges(bool include_inactive = false) const {
std::vector<edge_type> result;
result.reserve(_edge_count);
std::vector<char> used(_edge_count, false);
for (int v = 0; v < _n; v++) {
for (const auto& e : _g[v]) {
if (!include_inactive && !e.alive) continue;
if (0 <= e.id && e.id < _edge_count) {
if (used[e.id]) continue;
used[e.id] = true;
}
result.push_back(e);
}
}
return result;
}
Graph reversed() const {
Graph result(_n);
result._edge_count = _edge_count;
result._edge_positions.assign(_edge_count, {});
for (int v = 0; v < _n; v++) {
for (const auto& e : _g[v]) {
int idx = int(result._g[e.to].size());
result._g[e.to].push_back(edge_type(e.to, e.from, e.cost, e.id, e.alive));
if (0 <= e.id && e.id < _edge_count) result._edge_positions[e.id].push_back({e.to, idx});
}
}
return result;
}
};
} // namespace graph
} // namespace m1une
#line 9 "graph/cycle_detection.hpp"
namespace m1une {
namespace graph {
struct Cycle {
std::vector<int> vertices;
std::vector<int> edge_ids;
bool empty() const {
return vertices.empty();
}
};
inline Cycle restore_cycle(int from, int to, int closing_edge, const std::vector<int>& parent,
const std::vector<int>& parent_edge) {
Cycle result;
result.vertices.push_back(to);
std::vector<int> middle_vertices;
std::vector<int> middle_edges;
for (int v = from; v != to; v = parent[v]) {
middle_vertices.push_back(v);
middle_edges.push_back(parent_edge[v]);
}
std::reverse(middle_vertices.begin(), middle_vertices.end());
std::reverse(middle_edges.begin(), middle_edges.end());
result.vertices.insert(result.vertices.end(), middle_vertices.begin(), middle_vertices.end());
result.vertices.push_back(to);
result.edge_ids.insert(result.edge_ids.end(), middle_edges.begin(), middle_edges.end());
result.edge_ids.push_back(closing_edge);
return result;
}
template <class T>
Cycle find_directed_cycle(const Graph<T>& g) {
int n = g.size();
std::vector<int> color(n, 0), parent(n, -1), parent_edge(n, -1);
struct Frame {
int vertex;
std::size_t next_edge;
};
std::vector<Frame> stack;
stack.reserve(n);
for (int start = 0; start < n; start++) {
if (color[start] != 0) continue;
color[start] = 1;
stack.push_back(Frame{start, 0});
while (!stack.empty()) {
Frame& frame = stack.back();
const int vertex = frame.vertex;
const auto& adjacency = g[vertex];
while (
frame.next_edge < adjacency.size() &&
!adjacency[frame.next_edge].alive
) {
frame.next_edge++;
}
if (frame.next_edge == adjacency.size()) {
color[vertex] = 2;
stack.pop_back();
continue;
}
const auto& edge = adjacency[frame.next_edge++];
const int to = edge.to;
const int edge_id = edge.id;
if (color[to] == 0) {
parent[to] = vertex;
parent_edge[to] = edge_id;
color[to] = 1;
stack.push_back(Frame{to, 0});
} else if (color[to] == 1) {
return restore_cycle(vertex, to, edge_id, parent, parent_edge);
}
}
}
return Cycle();
}
template <class T>
Cycle find_undirected_cycle(const Graph<T>& g) {
int n = g.size();
std::vector<int> color(n, 0), parent(n, -1), parent_edge(n, -1);
struct Frame {
int vertex;
std::size_t next_edge;
};
std::vector<Frame> stack;
stack.reserve(n);
for (int start = 0; start < n; start++) {
if (color[start] != 0) continue;
color[start] = 1;
stack.push_back(Frame{start, 0});
while (!stack.empty()) {
Frame& frame = stack.back();
const int vertex = frame.vertex;
const auto& adjacency = g[vertex];
while (
frame.next_edge < adjacency.size() &&
(
!adjacency[frame.next_edge].alive ||
adjacency[frame.next_edge].id == parent_edge[vertex]
)
) {
frame.next_edge++;
}
if (frame.next_edge == adjacency.size()) {
color[vertex] = 2;
stack.pop_back();
continue;
}
const auto& edge = adjacency[frame.next_edge++];
const int to = edge.to;
const int edge_id = edge.id;
if (color[to] == 0) {
parent[to] = vertex;
parent_edge[to] = edge_id;
color[to] = 1;
stack.push_back(Frame{to, 0});
} else if (color[to] == 1) {
return restore_cycle(vertex, to, edge_id, parent, parent_edge);
}
}
}
return Cycle();
}
} // namespace graph
} // namespace m1une
#line 1 "graph/dag.hpp"
#line 1 "graph/dag_longest_path.hpp"
#line 6 "graph/dag_longest_path.hpp"
#include <limits>
#line 9 "graph/dag_longest_path.hpp"
#line 1 "graph/topological_sort.hpp"
#line 5 "graph/topological_sort.hpp"
#include <queue>
#line 7 "graph/topological_sort.hpp"
#line 9 "graph/topological_sort.hpp"
namespace m1une {
namespace graph {
template <class T>
std::optional<std::vector<int>> topological_sort(const Graph<T>& g) {
int n = g.size();
std::vector<int> indeg(n, 0);
for (int v = 0; v < n; v++) {
for (const auto& e : g[v]) {
if (!e.alive) continue;
indeg[e.to]++;
}
}
std::queue<int> que;
for (int v = 0; v < n; v++) {
if (indeg[v] == 0) que.push(v);
}
std::vector<int> order;
order.reserve(n);
while (!que.empty()) {
int v = que.front();
que.pop();
order.push_back(v);
for (const auto& e : g[v]) {
if (!e.alive) continue;
indeg[e.to]--;
if (indeg[e.to] == 0) que.push(e.to);
}
}
if (int(order.size()) != n) return std::nullopt;
return order;
}
template <class T>
bool is_dag(const Graph<T>& g) {
return topological_sort(g).has_value();
}
} // namespace graph
} // namespace m1une
#line 12 "graph/dag_longest_path.hpp"
namespace m1une {
namespace graph {
template <class T>
struct DagLongestPathResult {
std::vector<T> dist;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> topological_order;
T neg_inf;
bool reachable(int v) const {
assert(0 <= v && v < int(dist.size()));
return dist[v] != neg_inf;
}
std::vector<int> path(int t) const {
assert(reachable(t));
std::vector<int> result;
for (int v = t; v != -1; v = parent[v]) result.push_back(v);
std::reverse(result.begin(), result.end());
return result;
}
};
template <class T>
std::optional<DagLongestPathResult<T>> dag_longest_path(
const Graph<T>& g,
const std::vector<int>& sources,
T neg_inf = std::numeric_limits<T>::lowest() / T(4)
) {
const int n = g.size();
auto order = topological_sort(g);
if (!order) return std::nullopt;
DagLongestPathResult<T> result;
result.dist.assign(n, neg_inf);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.topological_order = *order;
result.neg_inf = neg_inf;
for (int s : sources) {
assert(0 <= s && s < n);
result.dist[s] = T(0);
}
for (int v : *order) {
if (result.dist[v] == neg_inf) continue;
for (const auto& e : g[v]) {
if (!e.alive) continue;
T nd = result.dist[v] + e.cost;
if (result.dist[e.to] >= nd) continue;
result.dist[e.to] = nd;
result.parent[e.to] = v;
result.parent_edge[e.to] = e.id;
}
}
return result;
}
template <class T>
std::optional<DagLongestPathResult<T>> dag_longest_path(
const Graph<T>& g,
int s,
T neg_inf = std::numeric_limits<T>::lowest() / T(4)
) {
return dag_longest_path(g, std::vector<int>{s}, neg_inf);
}
} // namespace graph
} // namespace m1une
#line 1 "graph/dag_path_count.hpp"
#line 7 "graph/dag_path_count.hpp"
#line 10 "graph/dag_path_count.hpp"
namespace m1une {
namespace graph {
template <class Count = long long, class T>
std::optional<std::vector<Count>> dag_path_count(
const Graph<T>& g,
const std::vector<int>& sources
) {
const int n = g.size();
auto order = topological_sort(g);
if (!order) return std::nullopt;
std::vector<Count> ways(n, Count(0));
std::vector<char> used_source(n, false);
for (int s : sources) {
assert(0 <= s && s < n);
if (used_source[s]) continue;
used_source[s] = true;
ways[s] += Count(1);
}
for (int v : *order) {
for (const auto& e : g[v]) {
if (e.alive) ways[e.to] += ways[v];
}
}
return ways;
}
template <class Count = long long, class T>
std::optional<std::vector<Count>> dag_path_count(const Graph<T>& g, int s) {
return dag_path_count<Count>(g, std::vector<int>{s});
}
} // namespace graph
} // namespace m1une
#line 1 "graph/dag_path_cover.hpp"
#line 7 "graph/dag_path_cover.hpp"
#line 1 "graph/bipartite.hpp"
#line 13 "graph/bipartite.hpp"
#line 15 "graph/bipartite.hpp"
namespace m1une {
namespace graph {
struct BipartiteResult {
bool is_bipartite;
std::vector<int> color;
std::vector<int> left_vertices;
std::vector<int> right_vertices;
std::vector<int> left_id;
std::vector<int> right_id;
};
template <class T>
BipartiteResult bipartite(const Graph<T>& g) {
int n = g.size();
BipartiteResult result;
result.is_bipartite = true;
result.color.assign(n, -1);
result.left_id.assign(n, -1);
result.right_id.assign(n, -1);
std::vector<std::vector<int>> adjacency(n);
for (const auto& e : g.edges()) {
adjacency[e.from].push_back(e.to);
adjacency[e.to].push_back(e.from);
}
std::queue<int> que;
for (int s = 0; s < n; s++) {
if (result.color[s] != -1) continue;
result.color[s] = 0;
que.push(s);
while (!que.empty()) {
int v = que.front();
que.pop();
for (int to : adjacency[v]) {
if (result.color[to] == -1) {
result.color[to] = result.color[v] ^ 1;
que.push(to);
} else if (result.color[to] == result.color[v]) {
result.is_bipartite = false;
return result;
}
}
}
}
for (int v = 0; v < n; v++) {
if (result.color[v] == 0) {
result.left_id[v] = int(result.left_vertices.size());
result.left_vertices.push_back(v);
} else {
result.right_id[v] = int(result.right_vertices.size());
result.right_vertices.push_back(v);
}
}
return result;
}
template <class T>
bool is_bipartite(const Graph<T>& g) {
return bipartite(g).is_bipartite;
}
struct BipartiteVertexSet {
std::vector<int> left;
std::vector<int> right;
int size() const {
return int(left.size() + right.size());
}
};
struct BipartiteMatching {
struct Edge {
int left;
int right;
int id;
bool alive;
};
struct Pair {
int left;
int right;
int edge_id;
};
private:
int _left_size;
int _right_size;
std::vector<Edge> _edges;
std::vector<std::vector<int>> _adj;
std::vector<std::vector<int>> _radj;
std::vector<int> _left_match;
std::vector<int> _right_match;
std::vector<int> _left_match_edge;
std::vector<int> _right_match_edge;
bool _calculated;
void invalidate() {
_calculated = false;
}
void ensure_matching() {
if (!_calculated) max_matching();
}
public:
BipartiteMatching() : BipartiteMatching(0, 0) {}
BipartiteMatching(int left_size, int right_size)
: _left_size(left_size),
_right_size(right_size),
_adj(left_size),
_radj(right_size),
_left_match(left_size, -1),
_right_match(right_size, -1),
_left_match_edge(left_size, -1),
_right_match_edge(right_size, -1),
_calculated(false) {
assert(0 <= left_size);
assert(0 <= right_size);
}
int left_size() const {
return _left_size;
}
int right_size() const {
return _right_size;
}
int edge_count() const {
return int(_edges.size());
}
int add_edge(int left, int right) {
assert(0 <= left && left < _left_size);
assert(0 <= right && right < _right_size);
int id = int(_edges.size());
_edges.push_back(Edge{left, right, id, true});
_adj[left].push_back(id);
_radj[right].push_back(id);
invalidate();
return id;
}
Edge get_edge(int i) const {
assert(0 <= i && i < int(_edges.size()));
return _edges[i];
}
std::vector<Edge> edges(bool include_inactive = false) const {
std::vector<Edge> result;
result.reserve(_edges.size());
for (const auto& e : _edges) {
if (include_inactive || e.alive) result.push_back(e);
}
return result;
}
void set_edge_alive(int id, bool alive) {
assert(0 <= id && id < int(_edges.size()));
_edges[id].alive = alive;
invalidate();
}
void erase_edge(int id) {
set_edge_alive(id, false);
}
void revive_edge(int id) {
set_edge_alive(id, true);
}
bool is_edge_alive(int id) const {
assert(0 <= id && id < int(_edges.size()));
return _edges[id].alive;
}
int max_matching() {
_left_match.assign(_left_size, -1);
_right_match.assign(_right_size, -1);
_left_match_edge.assign(_left_size, -1);
_right_match_edge.assign(_right_size, -1);
std::vector<int> dist(_left_size);
auto bfs = [&]() -> bool {
std::queue<int> que;
bool found = false;
for (int l = 0; l < _left_size; l++) {
if (_left_match[l] == -1) {
dist[l] = 0;
que.push(l);
} else {
dist[l] = -1;
}
}
while (!que.empty()) {
int l = que.front();
que.pop();
for (int id : _adj[l]) {
const auto& e = _edges[id];
if (!e.alive) continue;
int next_left = _right_match[e.right];
if (next_left == -1) {
found = true;
} else if (dist[next_left] == -1) {
dist[next_left] = dist[l] + 1;
que.push(next_left);
}
}
}
return found;
};
auto dfs = [&](auto self, int l) -> bool {
for (int id : _adj[l]) {
const auto& e = _edges[id];
if (!e.alive) continue;
int next_left = _right_match[e.right];
if (next_left != -1 && (dist[next_left] != dist[l] + 1 || !self(self, next_left))) {
continue;
}
_left_match[l] = e.right;
_right_match[e.right] = l;
_left_match_edge[l] = id;
_right_match_edge[e.right] = id;
return true;
}
dist[l] = -1;
return false;
};
int result = 0;
while (bfs()) {
for (int l = 0; l < _left_size; l++) {
if (_left_match[l] == -1 && dfs(dfs, l)) result++;
}
}
_calculated = true;
return result;
}
int matching_size() {
ensure_matching();
int result = 0;
for (int right : _left_match) {
if (right != -1) result++;
}
return result;
}
std::vector<int> left_match() {
ensure_matching();
return _left_match;
}
std::vector<int> right_match() {
ensure_matching();
return _right_match;
}
std::vector<Pair> matching() {
ensure_matching();
std::vector<Pair> result;
for (int l = 0; l < _left_size; l++) {
if (_left_match[l] != -1) result.push_back(Pair{l, _left_match[l], _left_match_edge[l]});
}
return result;
}
BipartiteVertexSet minimum_vertex_cover() {
ensure_matching();
std::vector<char> visited_left(_left_size, false), visited_right(_right_size, false);
std::queue<int> que;
for (int l = 0; l < _left_size; l++) {
if (_left_match[l] == -1) {
visited_left[l] = true;
que.push(l);
}
}
while (!que.empty()) {
int l = que.front();
que.pop();
for (int id : _adj[l]) {
const auto& e = _edges[id];
if (!e.alive || _left_match_edge[l] == id || visited_right[e.right]) continue;
visited_right[e.right] = true;
int next_left = _right_match[e.right];
if (next_left != -1 && !visited_left[next_left]) {
visited_left[next_left] = true;
que.push(next_left);
}
}
}
BipartiteVertexSet result;
for (int l = 0; l < _left_size; l++) {
if (!visited_left[l]) result.left.push_back(l);
}
for (int r = 0; r < _right_size; r++) {
if (visited_right[r]) result.right.push_back(r);
}
return result;
}
BipartiteVertexSet maximum_independent_set() {
auto cover = minimum_vertex_cover();
std::vector<char> in_left_cover(_left_size, false), in_right_cover(_right_size, false);
for (int l : cover.left) in_left_cover[l] = true;
for (int r : cover.right) in_right_cover[r] = true;
BipartiteVertexSet result;
for (int l = 0; l < _left_size; l++) {
if (!in_left_cover[l]) result.left.push_back(l);
}
for (int r = 0; r < _right_size; r++) {
if (!in_right_cover[r]) result.right.push_back(r);
}
return result;
}
std::optional<std::vector<int>> minimum_edge_cover() {
ensure_matching();
std::vector<int> result;
std::vector<char> covered_left(_left_size, false), covered_right(_right_size, false);
std::vector<char> used_edge(_edges.size(), false);
auto use_edge = [&](int id) {
if (used_edge[id]) return;
used_edge[id] = true;
result.push_back(id);
covered_left[_edges[id].left] = true;
covered_right[_edges[id].right] = true;
};
for (int l = 0; l < _left_size; l++) {
if (_left_match_edge[l] != -1) use_edge(_left_match_edge[l]);
}
for (int l = 0; l < _left_size; l++) {
if (covered_left[l]) continue;
int id = -1;
for (int edge_id : _adj[l]) {
if (_edges[edge_id].alive) {
id = edge_id;
break;
}
}
if (id == -1) return std::nullopt;
use_edge(id);
}
for (int r = 0; r < _right_size; r++) {
if (covered_right[r]) continue;
int id = -1;
for (int edge_id : _radj[r]) {
if (_edges[edge_id].alive) {
id = edge_id;
break;
}
}
if (id == -1) return std::nullopt;
use_edge(id);
}
return result;
}
};
struct BipartiteMatchingGraph {
BipartiteResult parts;
BipartiteMatching matching;
std::vector<int> original_edge_id;
int left_vertex(int left) const {
assert(0 <= left && left < int(parts.left_vertices.size()));
return parts.left_vertices[left];
}
int right_vertex(int right) const {
assert(0 <= right && right < int(parts.right_vertices.size()));
return parts.right_vertices[right];
}
int original_edge(int edge_id) const {
assert(0 <= edge_id && edge_id < int(original_edge_id.size()));
return original_edge_id[edge_id];
}
};
template <class T>
std::optional<BipartiteMatchingGraph> make_bipartite_matching(const Graph<T>& g) {
auto parts = bipartite(g);
if (!parts.is_bipartite) return std::nullopt;
BipartiteMatchingGraph result;
result.parts = parts;
result.matching = BipartiteMatching(int(parts.left_vertices.size()), int(parts.right_vertices.size()));
for (const auto& e : g.edges()) {
int left, right;
if (parts.color[e.from] == 0) {
left = parts.left_id[e.from];
right = parts.right_id[e.to];
} else {
left = parts.left_id[e.to];
right = parts.right_id[e.from];
}
int id = result.matching.add_edge(left, right);
if (int(result.original_edge_id.size()) <= id) result.original_edge_id.resize(id + 1);
result.original_edge_id[id] = e.id;
}
return result;
}
struct BipartiteEdgeColoringResult {
int color_count;
std::vector<int> color;
};
namespace detail {
struct BipartiteEdgeColoringGroups {
int count;
std::vector<int> group;
};
inline BipartiteEdgeColoringGroups group_vertices(
const std::vector<int>& degree,
int maximum_degree
) {
BipartiteEdgeColoringGroups result;
result.count = 0;
result.group.assign(degree.size(), -1);
int current_degree = 0;
for (int vertex = 0; vertex < int(degree.size()); vertex++) {
if (degree[vertex] == 0) continue;
if (result.count == 0 || current_degree + degree[vertex] > maximum_degree) {
result.count++;
current_degree = 0;
}
result.group[vertex] = result.count - 1;
current_degree += degree[vertex];
}
return result;
}
class BipartiteEdgeColoringSolver {
private:
int _side_size;
int _original_edge_count;
std::vector<int> _left;
std::vector<int> _right;
std::vector<int> _color;
std::vector<int> _used_stamp;
int _stamp;
int other_endpoint(int vertex, int edge) const {
if (vertex < _side_size) return _side_size + _right[edge];
return _left[edge];
}
std::vector<int> perfect_matching(const std::vector<int>& edge_ids) const {
std::vector<std::vector<int>> adjacency(_side_size);
for (int edge : edge_ids) adjacency[_left[edge]].push_back(edge);
std::vector<int> right_match(_side_size, -1);
std::vector<int> left_match_edge(_side_size, -1);
int matching_size = 0;
for (int left = 0; left < _side_size; left++) {
for (int edge : adjacency[left]) {
int right = _right[edge];
if (right_match[right] != -1) continue;
right_match[right] = left;
left_match_edge[left] = edge;
matching_size++;
break;
}
}
std::vector<int> distance(_side_size);
std::vector<int> next_edge(_side_size);
std::vector<int> left_stack;
std::vector<int> path_edges;
left_stack.reserve(_side_size);
path_edges.reserve(_side_size);
while (matching_size < _side_size) {
std::queue<int> queue;
std::fill(distance.begin(), distance.end(), -1);
for (int left = 0; left < _side_size; left++) {
if (left_match_edge[left] != -1) continue;
distance[left] = 0;
queue.push(left);
}
bool reachable_free_right = false;
while (!queue.empty()) {
int left = queue.front();
queue.pop();
for (int edge : adjacency[left]) {
int next_left = right_match[_right[edge]];
if (next_left == -1) {
reachable_free_right = true;
} else if (distance[next_left] == -1) {
distance[next_left] = distance[left] + 1;
queue.push(next_left);
}
}
}
assert(reachable_free_right);
std::fill(next_edge.begin(), next_edge.end(), 0);
int augmented = 0;
for (int root = 0; root < _side_size; root++) {
if (left_match_edge[root] != -1 || distance[root] == -1) continue;
left_stack.clear();
path_edges.clear();
left_stack.push_back(root);
bool found = false;
while (!left_stack.empty() && !found) {
int left = left_stack.back();
bool advanced = false;
while (next_edge[left] < int(adjacency[left].size())) {
int edge = adjacency[left][next_edge[left]++];
int right = _right[edge];
int next_left = right_match[right];
if (next_left == -1) {
left_match_edge[left] = edge;
right_match[right] = left;
for (int index = int(path_edges.size()) - 1; index >= 0; index--) {
int path_edge = path_edges[index];
int path_left = left_stack[index];
left_match_edge[path_left] = path_edge;
right_match[_right[path_edge]] = path_left;
}
found = true;
break;
}
if (distance[next_left] != distance[left] + 1) continue;
path_edges.push_back(edge);
left_stack.push_back(next_left);
advanced = true;
break;
}
if (found || advanced) continue;
distance[left] = -1;
left_stack.pop_back();
if (path_edges.size() == left_stack.size() && !path_edges.empty()) {
path_edges.pop_back();
}
}
if (found) augmented++;
}
assert(augmented > 0);
matching_size += augmented;
}
return left_match_edge;
}
std::pair<std::vector<int>, std::vector<int>> split_even(
const std::vector<int>& edge_ids
) {
std::vector<std::vector<int>> incidence(std::size_t(2) * _side_size);
for (int edge : edge_ids) {
incidence[_left[edge]].push_back(edge);
incidence[_side_size + _right[edge]].push_back(edge);
}
_stamp++;
assert(_stamp > 0);
std::vector<int> next_edge(std::size_t(2) * _side_size, 0);
std::vector<int> first;
std::vector<int> second;
first.reserve(edge_ids.size() / 2);
second.reserve(edge_ids.size() / 2);
for (int start = 0; start < 2 * _side_size; start++) {
while (true) {
while (next_edge[start] < int(incidence[start].size()) &&
_used_stamp[incidence[start][next_edge[start]]] == _stamp) {
next_edge[start]++;
}
if (next_edge[start] == int(incidence[start].size())) break;
int vertex = start;
bool parity = false;
do {
while (next_edge[vertex] < int(incidence[vertex].size()) &&
_used_stamp[incidence[vertex][next_edge[vertex]]] == _stamp) {
next_edge[vertex]++;
}
assert(next_edge[vertex] < int(incidence[vertex].size()));
int edge = incidence[vertex][next_edge[vertex]++];
_used_stamp[edge] = _stamp;
if (!parity) {
first.push_back(edge);
} else {
second.push_back(edge);
}
parity = !parity;
vertex = other_endpoint(vertex, edge);
} while (vertex != start);
assert(!parity);
}
}
assert(first.size() == second.size());
return {std::move(first), std::move(second)};
}
void color_regular(const std::vector<int>& edge_ids, int degree, int offset) {
assert(std::size_t(_side_size) * std::size_t(degree) == edge_ids.size());
if (degree == 0) return;
if (degree == 1) {
for (int edge : edge_ids) {
if (edge < _original_edge_count) _color[edge] = offset;
}
return;
}
if (degree % 2 == 1) {
std::vector<int> matching = perfect_matching(edge_ids);
_stamp++;
assert(_stamp > 0);
for (int edge : matching) {
_used_stamp[edge] = _stamp;
if (edge < _original_edge_count) _color[edge] = offset;
}
std::vector<int> remaining;
remaining.reserve(edge_ids.size() - matching.size());
for (int edge : edge_ids) {
if (_used_stamp[edge] != _stamp) remaining.push_back(edge);
}
color_regular(remaining, degree - 1, offset + 1);
return;
}
auto [first, second] = split_even(edge_ids);
color_regular(first, degree / 2, offset);
color_regular(second, degree / 2, offset + degree / 2);
}
public:
BipartiteEdgeColoringSolver(
int side_size,
int original_edge_count,
std::vector<int> left,
std::vector<int> right
)
: _side_size(side_size),
_original_edge_count(original_edge_count),
_left(std::move(left)),
_right(std::move(right)),
_color(original_edge_count, -1),
_used_stamp(_left.size(), 0),
_stamp(0) {}
std::vector<int> solve(int degree) {
std::vector<int> edge_ids(_left.size());
for (int edge = 0; edge < int(edge_ids.size()); edge++) edge_ids[edge] = edge;
color_regular(edge_ids, degree, 0);
for (int color : _color) assert(0 <= color && color < degree);
return _color;
}
};
} // namespace detail
// Returns an optimal edge coloring of a bipartite multigraph.
inline BipartiteEdgeColoringResult bipartite_edge_coloring(
int left_size,
int right_size,
const std::vector<std::pair<int, int>>& edges
) {
assert(left_size >= 0);
assert(right_size >= 0);
assert(edges.size() <= std::size_t(std::numeric_limits<int>::max()));
std::vector<int> left_degree(left_size, 0);
std::vector<int> right_degree(right_size, 0);
int maximum_degree = 0;
for (auto [left, right] : edges) {
assert(0 <= left && left < left_size);
assert(0 <= right && right < right_size);
left_degree[left]++;
right_degree[right]++;
maximum_degree = std::max(maximum_degree, left_degree[left]);
maximum_degree = std::max(maximum_degree, right_degree[right]);
}
BipartiteEdgeColoringResult result;
result.color_count = maximum_degree;
if (edges.empty()) return result;
detail::BipartiteEdgeColoringGroups left_groups =
detail::group_vertices(left_degree, maximum_degree);
detail::BipartiteEdgeColoringGroups right_groups =
detail::group_vertices(right_degree, maximum_degree);
int side_size = std::max(left_groups.count, right_groups.count);
std::vector<int> contracted_left;
std::vector<int> contracted_right;
contracted_left.reserve(std::size_t(3) * edges.size());
contracted_right.reserve(std::size_t(3) * edges.size());
std::vector<int> contracted_left_degree(side_size, 0);
std::vector<int> contracted_right_degree(side_size, 0);
for (auto [left, right] : edges) {
int contracted_left_vertex = left_groups.group[left];
int contracted_right_vertex = right_groups.group[right];
contracted_left.push_back(contracted_left_vertex);
contracted_right.push_back(contracted_right_vertex);
contracted_left_degree[contracted_left_vertex]++;
contracted_right_degree[contracted_right_vertex]++;
}
int left = 0;
int right = 0;
while (true) {
while (left < side_size && contracted_left_degree[left] == maximum_degree) left++;
while (right < side_size && contracted_right_degree[right] == maximum_degree) right++;
if (left == side_size || right == side_size) break;
contracted_left.push_back(left);
contracted_right.push_back(right);
contracted_left_degree[left]++;
contracted_right_degree[right]++;
}
assert(left == side_size && right == side_size);
assert(contracted_left.size() == std::size_t(side_size) * std::size_t(maximum_degree));
detail::BipartiteEdgeColoringSolver solver(
side_size,
int(edges.size()),
std::move(contracted_left),
std::move(contracted_right)
);
result.color = solver.solve(maximum_degree);
return result;
}
} // namespace graph
} // namespace m1une
#line 11 "graph/dag_path_cover.hpp"
namespace m1une {
namespace graph {
struct DagPathCoverResult {
std::vector<std::vector<int>> paths;
std::vector<std::vector<int>> path_edge_ids;
std::vector<int> predecessor;
std::vector<int> successor;
std::vector<int> predecessor_edge;
std::vector<int> successor_edge;
int size() const {
return int(paths.size());
}
};
template <class T>
std::optional<DagPathCoverResult> minimum_dag_path_cover(const Graph<T>& g) {
const int n = g.size();
if (!topological_sort(g)) return std::nullopt;
BipartiteMatching matching(n, n);
std::vector<int> original_edge_id;
for (int v = 0; v < n; v++) {
for (const auto& e : g[v]) {
if (!e.alive) continue;
matching.add_edge(v, e.to);
original_edge_id.push_back(e.id);
}
}
DagPathCoverResult result;
result.predecessor.assign(n, -1);
result.successor.assign(n, -1);
result.predecessor_edge.assign(n, -1);
result.successor_edge.assign(n, -1);
for (const auto& pair : matching.matching()) {
const int edge_id = original_edge_id[pair.edge_id];
result.successor[pair.left] = pair.right;
result.successor_edge[pair.left] = edge_id;
result.predecessor[pair.right] = pair.left;
result.predecessor_edge[pair.right] = edge_id;
}
int covered = 0;
for (int s = 0; s < n; s++) {
if (result.predecessor[s] != -1) continue;
result.paths.emplace_back();
result.path_edge_ids.emplace_back();
for (int v = s; v != -1; v = result.successor[v]) {
result.paths.back().push_back(v);
covered++;
if (result.successor_edge[v] != -1) {
result.path_edge_ids.back().push_back(result.successor_edge[v]);
}
}
}
assert(covered == n);
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/dag_reachability.hpp"
#line 9 "graph/dag_reachability.hpp"
#line 1 "utilities/dynamic_bitset.hpp"
#line 9 "utilities/dynamic_bitset.hpp"
namespace m1une {
namespace utilities {
struct DynamicBitset {
private:
static constexpr int BITS_PER_BLOCK = 64;
static constexpr uint64_t FULL_BLOCK = ~uint64_t{0};
int _n;
std::vector<uint64_t> blocks;
static int block_count(int n) {
assert(n >= 0);
return (n + BITS_PER_BLOCK - 1) >> 6;
}
uint64_t tail_mask() const {
const int rem = _n & (BITS_PER_BLOCK - 1);
return rem == 0 ? FULL_BLOCK : ((uint64_t{1} << rem) - 1);
}
// Keep unused bits in the last block equal to zero.
void clean() {
if (!blocks.empty()) blocks.back() &= tail_mask();
}
public:
DynamicBitset() : _n(0), blocks() {}
explicit DynamicBitset(int n, bool val = false) : _n(n), blocks(block_count(n), val ? FULL_BLOCK : 0) {
if (val) clean();
}
// Returns the logical number of bits.
int size() const {
return _n;
}
// Returns whether the bit at index i is set.
bool test(int i) const {
assert(0 <= i && i < _n);
return (blocks[i >> 6] >> (i & (BITS_PER_BLOCK - 1))) & 1;
}
// Sets the bit at index i to true.
void set(int i) {
assert(0 <= i && i < _n);
blocks[i >> 6] |= uint64_t{1} << (i & (BITS_PER_BLOCK - 1));
}
// Sets all bits to true.
void set() {
std::fill(blocks.begin(), blocks.end(), FULL_BLOCK);
clean();
}
// Sets the bit at index i to false.
void reset(int i) {
assert(0 <= i && i < _n);
blocks[i >> 6] &= ~(uint64_t{1} << (i & (BITS_PER_BLOCK - 1)));
}
// Sets all bits to false.
void reset() {
std::fill(blocks.begin(), blocks.end(), uint64_t{0});
}
// Flips the bit at index i.
void flip(int i) {
assert(0 <= i && i < _n);
blocks[i >> 6] ^= uint64_t{1} << (i & (BITS_PER_BLOCK - 1));
}
// Flips all bits.
void flip() {
for (uint64_t& block : blocks) block = ~block;
clean();
}
// Returns the number of set bits.
int popcount() const {
int res = 0;
for (uint64_t block : blocks) res += __builtin_popcountll(block);
return res;
}
// Returns the index of the least significant set bit, or -1 if no bit is set.
int lowbit() const {
const int m = static_cast<int>(blocks.size());
for (int i = 0; i < m; ++i) {
if (blocks[i] != 0) return (i << 6) + __builtin_ctzll(blocks[i]);
}
return -1;
}
// Returns the index of the most significant set bit, or -1 if no bit is set.
int topbit() const {
for (int i = static_cast<int>(blocks.size()) - 1; i >= 0; --i) {
if (blocks[i] != 0) return (i << 6) + (BITS_PER_BLOCK - 1 - __builtin_clzll(blocks[i]));
}
return -1;
}
// Returns whether at least one bit is set.
bool any() const {
for (uint64_t block : blocks) {
if (block != 0) return true;
}
return false;
}
// Returns whether every logical bit is set.
bool all() const {
if (_n == 0) return true;
const int m = static_cast<int>(blocks.size());
for (int i = 0; i + 1 < m; ++i) {
if (blocks[i] != FULL_BLOCK) return false;
}
return blocks.back() == tail_mask();
}
// Returns whether no bit is set.
bool none() const {
return !any();
}
DynamicBitset& operator&=(const DynamicBitset& other) {
assert(_n == other._n);
const std::size_t m = blocks.size();
for (std::size_t i = 0; i < m; ++i) blocks[i] &= other.blocks[i];
return *this;
}
DynamicBitset& operator|=(const DynamicBitset& other) {
assert(_n == other._n);
const std::size_t m = blocks.size();
for (std::size_t i = 0; i < m; ++i) blocks[i] |= other.blocks[i];
return *this;
}
DynamicBitset& operator^=(const DynamicBitset& other) {
assert(_n == other._n);
const std::size_t m = blocks.size();
for (std::size_t i = 0; i < m; ++i) blocks[i] ^= other.blocks[i];
return *this;
}
DynamicBitset operator~() const {
DynamicBitset res = *this;
res.flip();
return res;
}
friend DynamicBitset operator&(DynamicBitset lhs, const DynamicBitset& rhs) {
lhs &= rhs;
return lhs;
}
friend DynamicBitset operator|(DynamicBitset lhs, const DynamicBitset& rhs) {
lhs |= rhs;
return lhs;
}
friend DynamicBitset operator^(DynamicBitset lhs, const DynamicBitset& rhs) {
lhs ^= rhs;
return lhs;
}
};
} // namespace utilities
} // namespace m1une
#line 13 "graph/dag_reachability.hpp"
namespace m1une {
namespace graph {
struct DagReachability {
std::vector<utilities::DynamicBitset> reachable_vertices;
std::vector<int> topological_order;
int size() const {
return int(reachable_vertices.size());
}
bool reachable(int from, int to) const {
assert(0 <= from && from < size());
assert(0 <= to && to < size());
return reachable_vertices[from].test(to);
}
};
template <class T>
std::optional<DagReachability> dag_reachability(const Graph<T>& g) {
const int n = g.size();
auto order = topological_sort(g);
if (!order) return std::nullopt;
DagReachability result;
result.reachable_vertices.assign(n, utilities::DynamicBitset(n));
result.topological_order = *order;
for (int i = n - 1; i >= 0; i--) {
int v = (*order)[i];
result.reachable_vertices[v].set(v);
for (const auto& e : g[v]) {
if (e.alive) result.reachable_vertices[v] |= result.reachable_vertices[e.to];
}
}
return result;
}
template <class T>
struct DagTransitiveReductionResult {
Graph<T> graph;
std::vector<int> original_edge_ids;
};
template <class T>
std::optional<DagTransitiveReductionResult<T>> dag_transitive_reduction(const Graph<T>& g) {
auto reachability = dag_reachability(g);
if (!reachability) return std::nullopt;
const int n = g.size();
std::vector<int> position(n);
for (int i = 0; i < n; i++) position[reachability->topological_order[i]] = i;
std::vector<char> kept(g.edge_count(), false);
for (int v = 0; v < n; v++) {
std::vector<const Edge<T>*> outgoing;
outgoing.reserve(g[v].size());
for (const auto& e : g[v]) {
if (e.alive) outgoing.push_back(&e);
}
std::stable_sort(outgoing.begin(), outgoing.end(), [&](const auto* lhs, const auto* rhs) {
return position[lhs->to] < position[rhs->to];
});
utilities::DynamicBitset covered(n);
for (const auto* e : outgoing) {
if (covered.test(e->to)) continue;
kept[e->id] = true;
covered |= reachability->reachable_vertices[e->to];
}
}
DagTransitiveReductionResult<T> result;
result.graph = Graph<T>(n);
for (int v = 0; v < n; v++) {
for (const auto& e : g[v]) {
if (!e.alive || !kept[e.id]) continue;
result.graph.add_directed_edge(e.from, e.to, e.cost);
result.original_edge_ids.push_back(e.id);
}
}
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/dag_shortest_path.hpp"
#line 9 "graph/dag_shortest_path.hpp"
#line 12 "graph/dag_shortest_path.hpp"
namespace m1une {
namespace graph {
template <class T>
struct DagShortestPathResult {
std::vector<T> dist;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> topological_order;
T inf;
bool reachable(int v) const {
assert(0 <= v && v < int(dist.size()));
return dist[v] != inf;
}
std::vector<int> path(int t) const {
assert(reachable(t));
std::vector<int> result;
for (int v = t; v != -1; v = parent[v]) result.push_back(v);
std::reverse(result.begin(), result.end());
return result;
}
};
template <class T>
std::optional<DagShortestPathResult<T>> dag_shortest_path(
const Graph<T>& g, const std::vector<int>& sources, T inf = std::numeric_limits<T>::max() / T(4)) {
int n = g.size();
auto order = topological_sort(g);
if (!order) return std::nullopt;
DagShortestPathResult<T> result;
result.dist.assign(n, inf);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.topological_order = *order;
result.inf = inf;
for (int s : sources) {
assert(0 <= s && s < n);
if (result.dist[s] == T(0)) continue;
result.dist[s] = T(0);
}
for (int v : *order) {
if (result.dist[v] == inf) continue;
for (const auto& e : g[v]) {
if (!e.alive) continue;
T nd = result.dist[v] + e.cost;
if (result.dist[e.to] <= nd) continue;
result.dist[e.to] = nd;
result.parent[e.to] = v;
result.parent_edge[e.to] = e.id;
}
}
return result;
}
template <class T>
std::optional<DagShortestPathResult<T>> dag_shortest_path(
const Graph<T>& g, int s, T inf = std::numeric_limits<T>::max() / T(4)) {
return dag_shortest_path(g, std::vector<int>{s}, inf);
}
} // namespace graph
} // namespace m1une
#line 10 "graph/dag.hpp"
#line 1 "graph/dfs.hpp"
#line 6 "graph/dfs.hpp"
#include <concepts>
#include <functional>
#line 10 "graph/dfs.hpp"
#line 12 "graph/dfs.hpp"
namespace m1une {
namespace graph {
struct DfsResult {
std::vector<int> depth;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> root;
std::vector<int> tin;
std::vector<int> tout;
std::vector<int> preorder;
std::vector<int> postorder;
std::vector<int> roots;
bool reachable(int vertex) const {
assert(0 <= vertex && vertex < int(depth.size()));
return depth[vertex] != -1;
}
int component_count() const {
return int(roots.size());
}
std::vector<int> path(int target) const {
assert(reachable(target));
std::vector<int> result;
for (int vertex = target; vertex != -1; vertex = parent[vertex]) {
result.push_back(vertex);
}
std::reverse(result.begin(), result.end());
return result;
}
bool is_ancestor(int ancestor, int vertex) const {
assert(0 <= ancestor && ancestor < int(depth.size()));
assert(0 <= vertex && vertex < int(depth.size()));
if (!reachable(ancestor) || !reachable(vertex)) return false;
return tin[ancestor] <= tin[vertex] && tout[vertex] <= tout[ancestor];
}
};
namespace dfs_detail {
template <class Callback>
concept DfsCallback =
std::invocable<Callback&, int, int> ||
std::invocable<Callback&, int>;
template <DfsCallback Callback>
void invoke_callback(Callback& callback, int vertex, int parent) {
if constexpr (std::invocable<Callback&, int, int>) {
std::invoke(callback, vertex, parent);
} else {
std::invoke(callback, vertex);
}
}
template <class T, class Callback>
DfsResult run_dfs(
const Graph<T>& graph,
const std::vector<int>& sources,
bool complete_forest,
Callback& callback
) {
const int n = graph.size();
DfsResult result;
result.depth.assign(n, -1);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.root.assign(n, -1);
result.tin.assign(n, -1);
result.tout.assign(n, -1);
result.preorder.reserve(n);
result.postorder.reserve(n);
result.roots.reserve(n);
struct Frame {
int vertex;
int next_edge;
};
std::vector<Frame> stack;
stack.reserve(n);
int timer = 0;
auto traverse = [&](int source) {
assert(0 <= source && source < n);
if (result.reachable(source)) return;
result.depth[source] = 0;
result.root[source] = source;
result.tin[source] = ++timer;
result.preorder.push_back(source);
result.roots.push_back(source);
invoke_callback(callback, source, -1);
stack.push_back(Frame{source, 0});
while (!stack.empty()) {
Frame& frame = stack.back();
int vertex = frame.vertex;
if (frame.next_edge == int(graph[vertex].size())) {
result.tout[vertex] = ++timer;
result.postorder.push_back(vertex);
stack.pop_back();
continue;
}
const Edge<T>& edge = graph[vertex][frame.next_edge++];
if (!edge.alive || result.reachable(edge.to)) continue;
result.depth[edge.to] = result.depth[vertex] + 1;
result.parent[edge.to] = vertex;
result.parent_edge[edge.to] = edge.id;
result.root[edge.to] = result.root[vertex];
result.tin[edge.to] = ++timer;
result.preorder.push_back(edge.to);
invoke_callback(callback, edge.to, vertex);
stack.push_back(Frame{edge.to, 0});
}
};
for (int source : sources) traverse(source);
if (complete_forest) {
for (int vertex = 0; vertex < n; vertex++) traverse(vertex);
}
return result;
}
} // namespace dfs_detail
template <class T>
DfsResult dfs(const Graph<T>& graph, const std::vector<int>& sources) {
auto callback = [](int) {};
return dfs_detail::run_dfs(graph, sources, false, callback);
}
template <class T>
DfsResult dfs(const Graph<T>& graph, int source) {
return dfs(graph, std::vector<int>{source});
}
template <class T>
DfsResult dfs(const Graph<T>& graph) {
auto callback = [](int) {};
return dfs_detail::run_dfs(
graph,
std::vector<int>(),
true,
callback
);
}
template <class T, class Callback>
requires dfs_detail::DfsCallback<Callback>
DfsResult dfs(
const Graph<T>& graph,
const std::vector<int>& sources,
Callback&& callback
) {
return dfs_detail::run_dfs(graph, sources, false, callback);
}
template <class T, class Callback>
requires dfs_detail::DfsCallback<Callback>
DfsResult dfs(const Graph<T>& graph, int source, Callback&& callback) {
return dfs(
graph,
std::vector<int>{source},
std::forward<Callback>(callback)
);
}
template <class T, class Callback>
requires dfs_detail::DfsCallback<Callback>
DfsResult dfs(const Graph<T>& graph, Callback&& callback) {
return dfs_detail::run_dfs(
graph,
std::vector<int>(),
true,
callback
);
}
} // namespace graph
} // namespace m1une
#line 1 "graph/directed_mst.hpp"
#line 8 "graph/directed_mst.hpp"
#line 10 "graph/directed_mst.hpp"
namespace m1une {
namespace graph {
template <class T>
struct DirectedMinimumSpanningTree {
T cost;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<Edge<T>> edges;
int root;
};
namespace internal {
template <class T>
struct DirectedMstEdge {
int from = -1;
int to = -1;
T cost = T(0);
int id = -1;
};
template <class T>
struct DirectedMstHeapPool {
using StoredEdge = DirectedMstEdge<T>;
struct Node {
StoredEdge edge;
T offset = T(0);
int child = -1;
int sibling = -1;
};
struct Heap {
int root = -1;
int size = 0;
};
std::vector<Node> nodes;
explicit DirectedMstHeapPool(int capacity = 0) {
nodes.reserve(capacity);
}
T key(int node) const {
return nodes[node].edge.cost + nodes[node].offset;
}
int meld_roots(int first, int second) {
if (first == -1) return second;
if (second == -1) return first;
if (key(second) < key(first)) std::swap(first, second);
nodes[second].offset -= nodes[first].offset;
nodes[second].sibling = nodes[first].child;
nodes[first].child = second;
return first;
}
void push(Heap& heap, const StoredEdge& edge) {
const int node = int(nodes.size());
nodes.push_back(Node{edge, T(0), -1, -1});
heap.root = meld_roots(heap.root, node);
heap.size++;
}
void meld(Heap& destination, Heap& source) {
destination.root = meld_roots(destination.root, source.root);
destination.size += source.size;
source.root = -1;
source.size = 0;
}
const StoredEdge& top(const Heap& heap) const {
assert(heap.root != -1);
return nodes[heap.root].edge;
}
T top_key(const Heap& heap) const {
assert(heap.root != -1);
return key(heap.root);
}
void add_all(Heap& heap, const T& delta) {
assert(heap.root != -1);
nodes[heap.root].offset += delta;
}
void pop(Heap& heap) {
assert(heap.root != -1 && heap.size > 0);
const int old_root = heap.root;
int child = nodes[old_root].child;
std::vector<int> pairs;
while (child != -1) {
int first = child;
child = nodes[first].sibling;
nodes[first].sibling = -1;
nodes[first].offset += nodes[old_root].offset;
if (child != -1) {
int second = child;
child = nodes[second].sibling;
nodes[second].sibling = -1;
nodes[second].offset += nodes[old_root].offset;
first = meld_roots(first, second);
}
pairs.push_back(first);
}
heap.root = -1;
for (auto it = pairs.rbegin(); it != pairs.rend(); ++it) {
heap.root = meld_roots(*it, heap.root);
}
heap.size--;
}
};
struct DirectedMstDsu {
std::vector<int> parent;
explicit DirectedMstDsu(int n) : parent(n, -1) {}
int leader(int vertex) {
int root = vertex;
while (parent[root] != -1) root = parent[root];
while (vertex != root) {
int next = parent[vertex];
parent[vertex] = root;
vertex = next;
}
return root;
}
};
template <class T>
struct DirectedMstRootlessCost {
int artificial_edges;
T original_cost;
DirectedMstRootlessCost() : artificial_edges(0), original_cost(T(0)) {}
explicit DirectedMstRootlessCost(int zero)
: artificial_edges(zero), original_cost(T(0)) {
assert(zero == 0);
}
DirectedMstRootlessCost(int artificial_edges_, const T& original_cost_)
: artificial_edges(artificial_edges_), original_cost(original_cost_) {}
DirectedMstRootlessCost& operator+=(const DirectedMstRootlessCost& other) {
artificial_edges += other.artificial_edges;
original_cost += other.original_cost;
return *this;
}
DirectedMstRootlessCost& operator-=(const DirectedMstRootlessCost& other) {
artificial_edges -= other.artificial_edges;
original_cost -= other.original_cost;
return *this;
}
friend DirectedMstRootlessCost operator+(
DirectedMstRootlessCost first,
const DirectedMstRootlessCost& second
) {
return first += second;
}
friend DirectedMstRootlessCost operator-(
DirectedMstRootlessCost first,
const DirectedMstRootlessCost& second
) {
return first -= second;
}
friend bool operator<(
const DirectedMstRootlessCost& first,
const DirectedMstRootlessCost& second
) {
if (first.artificial_edges != second.artificial_edges) {
return first.artificial_edges < second.artificial_edges;
}
return first.original_cost < second.original_cost;
}
};
} // namespace internal
// Returns a minimum-cost spanning arborescence rooted at root, or nullopt when
// some vertex is unreachable from the root using active directed edges.
template <class T>
std::optional<DirectedMinimumSpanningTree<T>> directed_mst(
const Graph<T>& graph,
int root
) {
const int n = graph.size();
assert(0 <= root && root < n);
const int maximum_node_count = 2 * n;
int active_edge_count = 0;
#ifndef NDEBUG
std::vector<int> incidence(graph.edge_count(), 0);
#endif
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
assert(0 <= edge.id && edge.id < graph.edge_count());
#ifndef NDEBUG
incidence[edge.id]++;
#endif
active_edge_count++;
}
}
#ifndef NDEBUG
for (int count : incidence) {
if (count != 0) assert(count == 1);
}
#endif
using StoredEdge = internal::DirectedMstEdge<T>;
using HeapPool = internal::DirectedMstHeapPool<T>;
HeapPool pool(active_edge_count);
std::vector<typename HeapPool::Heap> heaps(maximum_node_count);
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
pool.push(heaps[edge.to], StoredEdge{edge.from, edge.to, edge.cost, edge.id});
}
}
internal::DirectedMstDsu dsu(maximum_node_count);
std::vector<int> contraction_parent(maximum_node_count, -1);
std::vector<int> visited(maximum_node_count, 0);
std::vector<StoredEdge> selected(maximum_node_count);
int node_count = n;
int visit_token = 1;
visited[root] = 1;
for (int start = 0; start < n; start++) {
if (visited[start] != 0) continue;
visit_token++;
int component = start;
while (visited[component] == 0 || visited[component] == visit_token) {
if (visited[component] == visit_token) {
if (node_count == maximum_node_count) return std::nullopt;
const int contracted = node_count++;
int current = component;
do {
const T reduction = T(0) - pool.top_key(heaps[current]);
pool.add_all(heaps[current], reduction);
pool.meld(heaps[contracted], heaps[current]);
contraction_parent[current] = contracted;
dsu.parent[current] = contracted;
current = dsu.leader(selected[current].from);
} while (current != contracted);
component = contracted;
}
assert(visited[component] == 0);
visited[component] = visit_token;
while (heaps[component].size > 0 &&
dsu.leader(pool.top(heaps[component]).from) == component) {
pool.pop(heaps[component]);
}
if (heaps[component].size == 0) return std::nullopt;
selected[component] = pool.top(heaps[component]);
component = dsu.leader(selected[component].from);
}
}
DirectedMinimumSpanningTree<T> result;
result.cost = T(0);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.root = root;
result.parent[root] = root;
std::vector<char> expanded(node_count, false);
std::vector<StoredEdge> chosen(n);
for (int component = node_count - 1; component >= 0; component--) {
if (component == root || expanded[component]) continue;
const StoredEdge& edge = selected[component];
if (edge.id == -1) return std::nullopt;
int vertex = edge.to;
while (vertex != -1 && !expanded[vertex]) {
expanded[vertex] = true;
vertex = contraction_parent[vertex];
}
result.cost += edge.cost;
result.parent[edge.to] = edge.from;
result.parent_edge[edge.to] = edge.id;
chosen[edge.to] = edge;
}
result.edges.reserve(n - 1);
for (int vertex = 0; vertex < n; vertex++) {
if (vertex == root) continue;
if (result.parent[vertex] == -1) return std::nullopt;
const StoredEdge& edge = chosen[vertex];
result.edges.emplace_back(edge.from, edge.to, edge.cost, edge.id, true);
}
return result;
}
// Chooses the root that gives a minimum-cost spanning arborescence.
template <class T>
std::optional<DirectedMinimumSpanningTree<T>> directed_mst(
const Graph<T>& graph
) {
const int n = graph.size();
if (n == 0) return std::nullopt;
using Cost = internal::DirectedMstRootlessCost<T>;
Graph<Cost> augmented(n + 1);
std::vector<int> original_edge_id;
original_edge_id.reserve(graph.edge_count() + n);
#ifndef NDEBUG
std::vector<int> incidence(graph.edge_count(), 0);
#endif
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
#ifndef NDEBUG
assert(0 <= edge.id && edge.id < graph.edge_count());
incidence[edge.id]++;
#endif
augmented.add_directed_edge(
edge.from,
edge.to,
Cost(0, edge.cost)
);
original_edge_id.push_back(edge.id);
}
}
#ifndef NDEBUG
for (int count : incidence) {
if (count != 0) assert(count == 1);
}
#endif
const int artificial_root = n;
for (int vertex = 0; vertex < n; vertex++) {
augmented.add_directed_edge(
artificial_root,
vertex,
Cost(1, T(0))
);
original_edge_id.push_back(-1);
}
auto augmented_result = directed_mst(augmented, artificial_root);
if (!augmented_result || augmented_result->cost.artificial_edges != 1) {
return std::nullopt;
}
DirectedMinimumSpanningTree<T> result;
result.cost = augmented_result->cost.original_cost;
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.root = -1;
result.edges.reserve(n - 1);
for (int vertex = 0; vertex < n; vertex++) {
int augmented_edge_id = augmented_result->parent_edge[vertex];
assert(0 <= augmented_edge_id &&
augmented_edge_id < int(original_edge_id.size()));
int edge_id = original_edge_id[augmented_edge_id];
if (edge_id == -1) {
assert(result.root == -1);
result.root = vertex;
result.parent[vertex] = vertex;
continue;
}
result.parent[vertex] = augmented_result->parent[vertex];
result.parent_edge[vertex] = edge_id;
result.edges.emplace_back(
result.parent[vertex],
vertex,
augmented_result->edges[vertex].cost.original_cost,
edge_id,
true
);
}
assert(result.root != -1);
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/eulerian_trail.hpp"
#line 9 "graph/eulerian_trail.hpp"
#line 11 "graph/eulerian_trail.hpp"
namespace m1une {
namespace graph {
struct EulerianTrail {
std::vector<int> vertices;
std::vector<int> edge_ids;
int edge_count() const {
return int(edge_ids.size());
}
bool is_circuit() const {
return vertices.empty() || vertices.front() == vertices.back();
}
};
namespace internal {
template <class T>
std::optional<EulerianTrail> hierholzer(
const Graph<T>& graph,
int start,
int active_edge_count
) {
EulerianTrail result;
if (active_edge_count == 0) {
if (start != -1) result.vertices.push_back(start);
return result;
}
assert(0 <= start && start < graph.size());
std::vector<char> used(graph.edge_count(), false);
std::vector<int> cursor(graph.size(), 0);
std::vector<int> vertex_stack(1, start);
std::vector<int> incoming_edge_stack(1, -1);
std::vector<int> reversed_vertices;
std::vector<int> reversed_edges;
reversed_vertices.reserve(active_edge_count + 1);
reversed_edges.reserve(active_edge_count);
while (!vertex_stack.empty()) {
const int vertex = vertex_stack.back();
while (cursor[vertex] < int(graph[vertex].size())) {
const Edge<T>& edge = graph[vertex][cursor[vertex]];
if (edge.alive && !used[edge.id]) break;
cursor[vertex]++;
}
if (cursor[vertex] < int(graph[vertex].size())) {
const Edge<T>& edge = graph[vertex][cursor[vertex]++];
used[edge.id] = true;
vertex_stack.push_back(edge.to);
incoming_edge_stack.push_back(edge.id);
continue;
}
reversed_vertices.push_back(vertex);
const int incoming_edge = incoming_edge_stack.back();
if (incoming_edge != -1) reversed_edges.push_back(incoming_edge);
vertex_stack.pop_back();
incoming_edge_stack.pop_back();
}
if (int(reversed_edges.size()) != active_edge_count) return std::nullopt;
std::reverse(reversed_vertices.begin(), reversed_vertices.end());
std::reverse(reversed_edges.begin(), reversed_edges.end());
result.vertices = std::move(reversed_vertices);
result.edge_ids = std::move(reversed_edges);
return result;
}
template <class T>
std::vector<int> edge_incidence_count(const Graph<T>& graph) {
std::vector<int> count(graph.edge_count(), 0);
for (int vertex = 0; vertex < graph.size(); vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
assert(0 <= edge.id && edge.id < graph.edge_count());
count[edge.id]++;
}
}
return count;
}
} // namespace internal
template <class T>
std::optional<EulerianTrail> directed_eulerian_trail(
const Graph<T>& graph,
int start = -1
) {
assert(start == -1 || (0 <= start && start < graph.size()));
const int n = graph.size();
std::vector<int> incidence = internal::edge_incidence_count(graph);
std::vector<int> in_degree(n, 0);
std::vector<int> out_degree(n, 0);
int active_edge_count = 0;
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
out_degree[vertex]++;
in_degree[edge.to]++;
}
}
for (int count : incidence) {
if (count == 0) continue;
assert(count == 1);
active_edge_count++;
}
int required_start = -1;
int required_end = -1;
for (int vertex = 0; vertex < n; vertex++) {
const int difference = out_degree[vertex] - in_degree[vertex];
if (difference == 1) {
if (required_start != -1) return std::nullopt;
required_start = vertex;
} else if (difference == -1) {
if (required_end != -1) return std::nullopt;
required_end = vertex;
} else if (difference != 0) {
return std::nullopt;
}
}
if ((required_start == -1) != (required_end == -1)) return std::nullopt;
int chosen_start = start;
if (active_edge_count == 0) {
if (chosen_start == -1 && n > 0) chosen_start = 0;
return internal::hierholzer(graph, chosen_start, 0);
}
if (required_start != -1) {
if (chosen_start != -1 && chosen_start != required_start) return std::nullopt;
chosen_start = required_start;
} else if (chosen_start == -1) {
for (int vertex = 0; vertex < n; vertex++) {
if (out_degree[vertex] > 0) {
chosen_start = vertex;
break;
}
}
} else if (out_degree[chosen_start] == 0) {
return std::nullopt;
}
return internal::hierholzer(graph, chosen_start, active_edge_count);
}
template <class T>
std::optional<EulerianTrail> undirected_eulerian_trail(
const Graph<T>& graph,
int start = -1
) {
assert(start == -1 || (0 <= start && start < graph.size()));
const int n = graph.size();
std::vector<int> incidence = internal::edge_incidence_count(graph);
std::vector<int> degree(n, 0);
int active_edge_count = 0;
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (edge.alive) degree[vertex]++;
}
}
for (int count : incidence) {
if (count == 0) continue;
assert(count == 2);
active_edge_count++;
}
std::vector<int> odd;
for (int vertex = 0; vertex < n; vertex++) {
if (degree[vertex] & 1) odd.push_back(vertex);
}
if (!odd.empty() && odd.size() != 2) return std::nullopt;
int chosen_start = start;
if (active_edge_count == 0) {
if (chosen_start == -1 && n > 0) chosen_start = 0;
return internal::hierholzer(graph, chosen_start, 0);
}
if (odd.size() == 2) {
if (chosen_start != -1 && chosen_start != odd[0] && chosen_start != odd[1]) {
return std::nullopt;
}
if (chosen_start == -1) chosen_start = odd[0];
} else if (chosen_start == -1) {
for (int vertex = 0; vertex < n; vertex++) {
if (degree[vertex] > 0) {
chosen_start = vertex;
break;
}
}
} else if (degree[chosen_start] == 0) {
return std::nullopt;
}
return internal::hierholzer(graph, chosen_start, active_edge_count);
}
} // namespace graph
} // namespace m1une
#line 1 "graph/functional_graph.hpp"
#line 10 "graph/functional_graph.hpp"
namespace m1une {
namespace graph {
struct FunctionalGraph {
int component_count;
std::vector<int> successor;
std::vector<std::vector<int>> predecessors;
std::vector<std::vector<int>> cycles;
std::vector<int> component;
std::vector<int> component_size;
std::vector<int> cycle_entry;
std::vector<int> cycle_position;
std::vector<int> distance_to_cycle;
private:
std::vector<std::vector<int>> _up;
void check_vertex(int vertex) const {
assert(0 <= vertex && vertex < size());
}
int advance_before_cycle(int vertex, int steps) const {
assert(0 <= steps && steps <= distance_to_cycle[vertex]);
int bit = 0;
while (steps > 0) {
if (steps & 1) vertex = _up[bit][vertex];
steps >>= 1;
bit++;
}
return vertex;
}
public:
FunctionalGraph() : component_count(0) {}
explicit FunctionalGraph(const std::vector<int>& successor_) {
build(successor_);
}
void build(const std::vector<int>& successor_) {
successor = successor_;
const int n = size();
for (int to : successor) assert(0 <= to && to < n);
component_count = 0;
predecessors.assign(n, {});
cycles.clear();
component.assign(n, -1);
cycle_entry.assign(n, -1);
cycle_position.assign(n, -1);
distance_to_cycle.assign(n, -1);
std::vector<int> indegree(n, 0);
for (int vertex = 0; vertex < n; vertex++) {
predecessors[successor[vertex]].push_back(vertex);
indegree[successor[vertex]]++;
}
std::queue<int> queue;
std::vector<char> removed(n, false);
for (int vertex = 0; vertex < n; vertex++) {
if (indegree[vertex] == 0) queue.push(vertex);
}
while (!queue.empty()) {
const int vertex = queue.front();
queue.pop();
removed[vertex] = true;
const int to = successor[vertex];
indegree[to]--;
if (indegree[to] == 0) queue.push(to);
}
for (int start = 0; start < n; start++) {
if (removed[start] || component[start] != -1) continue;
const int component_id = int(cycles.size());
std::vector<int> cycle;
int vertex = start;
do {
const int position = int(cycle.size());
cycle.push_back(vertex);
component[vertex] = component_id;
cycle_entry[vertex] = vertex;
cycle_position[vertex] = position;
distance_to_cycle[vertex] = 0;
vertex = successor[vertex];
} while (vertex != start);
cycles.push_back(std::move(cycle));
}
component_count = int(cycles.size());
for (const std::vector<int>& cycle : cycles) {
for (int vertex : cycle) queue.push(vertex);
}
while (!queue.empty()) {
const int vertex = queue.front();
queue.pop();
for (int from : predecessors[vertex]) {
if (component[from] != -1) continue;
component[from] = component[vertex];
cycle_entry[from] = cycle_entry[vertex];
cycle_position[from] = cycle_position[vertex];
distance_to_cycle[from] = distance_to_cycle[vertex] + 1;
queue.push(from);
}
}
component_size.assign(component_count, 0);
for (int component_id : component) component_size[component_id]++;
int log = 1;
while ((std::uint64_t(1) << log) <= std::uint64_t(n)) log++;
_up.assign(log, successor);
for (int bit = 1; bit < log; bit++) {
for (int vertex = 0; vertex < n; vertex++) {
_up[bit][vertex] = _up[bit - 1][_up[bit - 1][vertex]];
}
}
}
int size() const {
return int(successor.size());
}
bool empty() const {
return successor.empty();
}
bool same_component(int first, int second) const {
check_vertex(first);
check_vertex(second);
return component[first] == component[second];
}
bool on_cycle(int vertex) const {
check_vertex(vertex);
return distance_to_cycle[vertex] == 0;
}
int cycle_size(int vertex) const {
check_vertex(vertex);
return int(cycles[component[vertex]].size());
}
int orbit_size(int vertex) const {
check_vertex(vertex);
return distance_to_cycle[vertex] + cycle_size(vertex);
}
int jump(int vertex, std::uint64_t steps) const {
check_vertex(vertex);
const int tail_length = distance_to_cycle[vertex];
if (steps < std::uint64_t(tail_length)) {
return advance_before_cycle(vertex, int(steps));
}
steps -= std::uint64_t(tail_length);
const int entry = cycle_entry[vertex];
const int length = cycle_size(entry);
const int offset = int(steps % std::uint64_t(length));
const int position = (cycle_position[entry] + offset) % length;
return cycles[component[vertex]][position];
}
long long distance(int from, int to) const {
check_vertex(from);
check_vertex(to);
if (!same_component(from, to)) return -1;
if (!on_cycle(to)) {
if (distance_to_cycle[from] < distance_to_cycle[to]) return -1;
const int difference = distance_to_cycle[from] - distance_to_cycle[to];
return advance_before_cycle(from, difference) == to ? difference : -1;
}
const int entry = cycle_entry[from];
const int length = cycle_size(from);
int cycle_distance = cycle_position[to] - cycle_position[entry];
if (cycle_distance < 0) cycle_distance += length;
return static_cast<long long>(distance_to_cycle[from]) + cycle_distance;
}
bool reachable(int from, int to) const {
return distance(from, to) != -1;
}
std::vector<int> path(int from, int to) const {
const long long path_length = distance(from, to);
if (path_length == -1) return {};
std::vector<int> result;
result.reserve(path_length + 1);
for (long long step = 0; step <= path_length; step++) {
result.push_back(from);
from = successor[from];
}
return result;
}
std::vector<int> orbit(int vertex) const {
check_vertex(vertex);
const int length = orbit_size(vertex);
std::vector<int> result;
result.reserve(length);
for (int step = 0; step < length; step++) {
result.push_back(vertex);
vertex = successor[vertex];
}
return result;
}
std::uint64_t visit_count(
int from,
int to,
std::uint64_t step_count
) const {
const long long first_visit = distance(from, to);
if (first_visit == -1 ||
std::uint64_t(first_visit) >= step_count) {
return 0;
}
if (!on_cycle(to)) return 1;
const std::uint64_t remaining =
step_count - 1 - std::uint64_t(first_visit);
return 1 + remaining / std::uint64_t(cycle_size(to));
}
long long first_meeting_time(int first, int second) const {
check_vertex(first);
check_vertex(second);
if (!same_component(first, second)) return -1;
if (first == second) return 0;
const int first_depth = distance_to_cycle[first];
const int second_depth = distance_to_cycle[second];
if (first_depth == second_depth &&
cycle_entry[first] == cycle_entry[second]) {
int elapsed = 0;
for (int bit = int(_up.size()) - 1; bit >= 0; bit--) {
const int steps = 1 << bit;
if (first_depth - elapsed < steps) continue;
const int next_first = _up[bit][first];
const int next_second = _up[bit][second];
if (next_first == next_second) continue;
first = next_first;
second = next_second;
elapsed += steps;
}
return elapsed + 1;
}
const int length = cycle_size(first);
int first_phase =
cycle_position[first] - first_depth % length;
int second_phase =
cycle_position[second] - second_depth % length;
if (first_phase < 0) first_phase += length;
if (second_phase < 0) second_phase += length;
if (first_phase != second_phase) return -1;
return std::max(first_depth, second_depth);
}
int first_meeting_vertex(int first, int second) const {
const long long time = first_meeting_time(first, second);
if (time == -1) return -1;
return jump(first, std::uint64_t(time));
}
};
} // namespace graph
} // namespace m1une
#line 1 "graph/incremental_scc.hpp"
#line 9 "graph/incremental_scc.hpp"
#line 11 "graph/incremental_scc.hpp"
namespace m1une {
namespace graph {
namespace incremental_scc_detail {
struct EdgeEvent {
int id;
int from;
int to;
};
inline std::vector<int> component_ids(
int vertex_count,
const std::vector<EdgeEvent>& edges,
int time
) {
std::vector<int> begin(vertex_count + 1, 0);
std::vector<int> reverse_begin(vertex_count + 1, 0);
int edge_count = 0;
for (const EdgeEvent& edge : edges) {
if (edge.id >= time) continue;
begin[edge.from + 1]++;
reverse_begin[edge.to + 1]++;
edge_count++;
}
for (int vertex = 0; vertex < vertex_count; vertex++) {
begin[vertex + 1] += begin[vertex];
reverse_begin[vertex + 1] += reverse_begin[vertex];
}
std::vector<int> adjacency(edge_count);
std::vector<int> reverse_adjacency(edge_count);
std::vector<int> cursor = begin;
std::vector<int> reverse_cursor = reverse_begin;
for (const EdgeEvent& edge : edges) {
if (edge.id >= time) continue;
adjacency[cursor[edge.from]++] = edge.to;
reverse_adjacency[reverse_cursor[edge.to]++] = edge.from;
}
std::vector<int>().swap(cursor);
std::vector<int>().swap(reverse_cursor);
std::vector<char> visited(vertex_count, false);
std::vector<int> next_position(begin.begin(), begin.end() - 1);
std::vector<int> order;
order.reserve(vertex_count);
std::vector<int> stack;
for (int start = 0; start < vertex_count; start++) {
if (visited[start]) continue;
visited[start] = true;
stack.push_back(start);
while (!stack.empty()) {
const int vertex = stack.back();
int& position = next_position[vertex];
if (position < begin[vertex + 1]) {
const int to = adjacency[position++];
if (!visited[to]) {
visited[to] = true;
stack.push_back(to);
}
} else {
order.push_back(vertex);
stack.pop_back();
}
}
}
std::vector<int> component(vertex_count, -1);
int component_count = 0;
for (auto iterator = order.rbegin(); iterator != order.rend(); ++iterator) {
const int start = *iterator;
if (component[start] != -1) continue;
component[start] = component_count;
stack.push_back(start);
while (!stack.empty()) {
const int vertex = stack.back();
stack.pop_back();
for (int position = reverse_begin[vertex];
position < reverse_begin[vertex + 1]; position++) {
const int to = reverse_adjacency[position];
if (component[to] != -1) continue;
component[to] = component_count;
stack.push_back(to);
}
}
component_count++;
}
return component;
}
} // namespace incremental_scc_detail
// For every directed edge e, returns the first time t after e is inserted such
// that its endpoints are in the same SCC. At time t, edges with IDs less than
// t have been inserted. edge_count() + 1 means this never happens.
template <class T>
std::vector<int> incremental_scc(const Graph<T>& graph) {
using incremental_scc_detail::EdgeEvent;
using incremental_scc_detail::component_ids;
const int vertex_count = graph.size();
const int edge_count = graph.edge_count();
const int never = edge_count + 1;
std::vector<int> merge_time(edge_count, never);
if (edge_count == 0) return merge_time;
std::vector<EdgeEvent> edges_by_id(edge_count);
std::vector<char> initialized(edge_count, false);
for (int vertex = 0; vertex < vertex_count; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
assert(0 <= edge.id && edge.id < edge_count);
assert(!initialized[edge.id]);
if (initialized[edge.id]) continue;
initialized[edge.id] = true;
edges_by_id[edge.id] = EdgeEvent{edge.id, edge.from, edge.to};
}
}
std::vector<EdgeEvent> events;
events.reserve(edge_count);
for (int edge_id = 0; edge_id < edge_count; edge_id++) {
assert(initialized[edge_id]);
if (graph.is_edge_alive(edge_id)) {
events.push_back(edges_by_id[edge_id]);
}
}
std::vector<EdgeEvent>().swap(edges_by_id);
std::vector<char>().swap(initialized);
std::vector<int> new_index(vertex_count, -1);
auto divide = [&](
auto&& self,
std::vector<EdgeEvent> current,
int left,
int right
) -> void {
if (current.empty() || right == left + 1) return;
const int middle = left + (right - left) / 2;
std::vector<int> touched;
touched.reserve(std::min(
std::size_t(vertex_count),
current.size() * 2
));
int compressed_count = 0;
for (const EdgeEvent& edge : current) {
if (new_index[edge.from] == -1) {
new_index[edge.from] = compressed_count++;
touched.push_back(edge.from);
}
if (new_index[edge.to] == -1) {
new_index[edge.to] = compressed_count++;
touched.push_back(edge.to);
}
}
for (EdgeEvent& edge : current) {
edge.from = new_index[edge.from];
edge.to = new_index[edge.to];
}
for (int vertex : touched) new_index[vertex] = -1;
std::vector<EdgeEvent> earlier;
std::vector<EdgeEvent> later;
earlier.reserve(current.size() / 2);
later.reserve(current.size() / 2);
{
std::vector<int> component =
component_ids(compressed_count, current, middle);
for (const EdgeEvent& edge : current) {
const int from_component = component[edge.from];
const int to_component = component[edge.to];
if (edge.id < middle &&
from_component == to_component) {
merge_time[edge.id] =
std::min(merge_time[edge.id], middle);
earlier.push_back(edge);
} else {
later.push_back(EdgeEvent{
edge.id,
from_component,
to_component
});
}
}
}
std::vector<EdgeEvent>().swap(current);
self(self, std::move(earlier), left, middle);
self(self, std::move(later), middle, right);
};
divide(divide, std::move(events), 0, edge_count + 1);
return merge_time;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/matrix_tree_theorem.hpp"
#line 7 "graph/matrix_tree_theorem.hpp"
#line 1 "math/matrix/linear_algebra.hpp"
#line 7 "math/matrix/linear_algebra.hpp"
#line 1 "math/matrix/matrix.hpp"
#line 9 "math/matrix/matrix.hpp"
namespace m1une {
namespace matrix {
template <class T>
class Matrix {
private:
int _rows;
int _cols;
std::vector<T> _data;
static std::size_t storage_size(int rows, int cols) {
assert(rows >= 0);
assert(cols >= 0);
return std::size_t(rows) * std::size_t(cols);
}
public:
using value_type = T;
Matrix() : _rows(0), _cols(0) {}
Matrix(int rows, int cols, const T& value = T())
: _rows(rows), _cols(cols), _data(storage_size(rows, cols), value) {}
Matrix(int rows, int cols, std::vector<T> values)
: _rows(rows), _cols(cols), _data(std::move(values)) {
assert(rows >= 0);
assert(cols >= 0);
assert(_data.size() == std::size_t(rows) * std::size_t(cols));
}
explicit Matrix(const std::vector<std::vector<T>>& values)
: _rows(int(values.size())), _cols(values.empty() ? 0 : int(values[0].size())),
_data(storage_size(_rows, _cols)) {
for (int row = 0; row < _rows; row++) {
assert(int(values[std::size_t(row)].size()) == _cols);
for (int col = 0; col < _cols; col++) {
(*this)[row][col] = values[std::size_t(row)][std::size_t(col)];
}
}
}
int rows() const {
return _rows;
}
int cols() const {
return _cols;
}
bool empty() const {
return _rows == 0 || _cols == 0;
}
std::vector<T>& data() {
return _data;
}
const std::vector<T>& data() const {
return _data;
}
T* operator[](int row) {
assert(0 <= row && row < _rows);
return _data.data() + std::size_t(row) * std::size_t(_cols);
}
const T* operator[](int row) const {
assert(0 <= row && row < _rows);
return _data.data() + std::size_t(row) * std::size_t(_cols);
}
T& operator()(int row, int col) {
assert(0 <= col && col < _cols);
return (*this)[row][col];
}
const T& operator()(int row, int col) const {
assert(0 <= col && col < _cols);
return (*this)[row][col];
}
static Matrix identity(int size) {
assert(size >= 0);
Matrix result(size, size);
for (int i = 0; i < size; i++) result[i][i] = T(1);
return result;
}
Matrix transposed() const {
Matrix result(_cols, _rows);
for (int row = 0; row < _rows; row++) {
for (int col = 0; col < _cols; col++) {
result[col][row] = (*this)[row][col];
}
}
return result;
}
void swap_rows(int first, int second) {
assert(0 <= first && first < _rows);
assert(0 <= second && second < _rows);
if (first == second) return;
for (int col = 0; col < _cols; col++) {
std::swap((*this)[first][col], (*this)[second][col]);
}
}
Matrix& operator+=(const Matrix& rhs) {
assert(_rows == rhs._rows && _cols == rhs._cols);
for (std::size_t i = 0; i < _data.size(); i++) _data[i] += rhs._data[i];
return *this;
}
Matrix& operator-=(const Matrix& rhs) {
assert(_rows == rhs._rows && _cols == rhs._cols);
for (std::size_t i = 0; i < _data.size(); i++) _data[i] -= rhs._data[i];
return *this;
}
Matrix& operator*=(const T& scalar) {
for (T& value : _data) value *= scalar;
return *this;
}
Matrix& operator/=(const T& scalar) {
for (T& value : _data) value /= scalar;
return *this;
}
Matrix& operator*=(const Matrix& rhs) {
return *this = *this * rhs;
}
Matrix operator+() const {
return *this;
}
Matrix operator-() const {
Matrix result = *this;
for (T& value : result._data) value = T() - value;
return result;
}
friend Matrix operator+(Matrix lhs, const Matrix& rhs) {
return lhs += rhs;
}
friend Matrix operator-(Matrix lhs, const Matrix& rhs) {
return lhs -= rhs;
}
friend Matrix operator*(Matrix lhs, const T& rhs) {
return lhs *= rhs;
}
friend Matrix operator*(const T& lhs, Matrix rhs) {
return rhs *= lhs;
}
friend Matrix operator/(Matrix lhs, const T& rhs) {
return lhs /= rhs;
}
friend Matrix operator*(const Matrix& lhs, const Matrix& rhs) {
assert(lhs._cols == rhs._rows);
Matrix result(lhs._rows, rhs._cols);
for (int row = 0; row < lhs._rows; row++) {
T* output = result[row];
for (int middle = 0; middle < lhs._cols; middle++) {
const T coefficient = lhs[row][middle];
if (coefficient == T()) continue;
const T* input = rhs[middle];
for (int col = 0; col < rhs._cols; col++) {
output[col] += coefficient * input[col];
}
}
}
return result;
}
friend std::vector<T> operator*(const Matrix& lhs, const std::vector<T>& rhs) {
assert(lhs._cols == int(rhs.size()));
std::vector<T> result(std::size_t(lhs._rows));
for (int row = 0; row < lhs._rows; row++) {
T value = T();
for (int col = 0; col < lhs._cols; col++) {
value += lhs[row][col] * rhs[std::size_t(col)];
}
result[std::size_t(row)] = value;
}
return result;
}
friend std::vector<T> operator*(const std::vector<T>& lhs, const Matrix& rhs) {
assert(int(lhs.size()) == rhs._rows);
std::vector<T> result(std::size_t(rhs._cols));
for (int row = 0; row < rhs._rows; row++) {
if (lhs[std::size_t(row)] == T()) continue;
for (int col = 0; col < rhs._cols; col++) {
result[std::size_t(col)] += lhs[std::size_t(row)] * rhs[row][col];
}
}
return result;
}
bool operator==(const Matrix& rhs) const {
return _rows == rhs._rows && _cols == rhs._cols && _data == rhs._data;
}
bool operator!=(const Matrix& rhs) const {
return !(*this == rhs);
}
Matrix pow(std::uint64_t exponent) const {
assert(_rows == _cols);
Matrix result = identity(_rows);
Matrix base = *this;
while (exponent > 0) {
if (exponent & 1) result *= base;
exponent >>= 1;
if (exponent > 0) base *= base;
}
return result;
}
};
} // namespace matrix
} // namespace m1une
#line 9 "math/matrix/linear_algebra.hpp"
namespace m1une {
namespace matrix {
template <class T>
constexpr T default_epsilon() {
if constexpr (std::is_floating_point_v<T>) {
return T(1e-10);
} else {
return T();
}
}
namespace detail {
template <class T>
T matrix_abs(T value) {
return value < T() ? T() - value : value;
}
template <class T>
bool is_zero(const T& value, const T& eps) {
if constexpr (std::is_floating_point_v<T>) {
return matrix_abs(value) <= eps;
} else {
(void)eps;
return value == T();
}
}
template <class T>
int choose_pivot(const Matrix<T>& matrix, int first_row, int col, const T& eps) {
int pivot = -1;
if constexpr (std::is_floating_point_v<T>) {
for (int row = first_row; row < matrix.rows(); row++) {
if (is_zero(matrix[row][col], eps)) continue;
if (pivot == -1 || matrix_abs(matrix[pivot][col]) < matrix_abs(matrix[row][col])) {
pivot = row;
}
}
} else {
for (int row = first_row; row < matrix.rows(); row++) {
if (!is_zero(matrix[row][col], eps)) {
pivot = row;
break;
}
}
}
return pivot;
}
template <class T>
std::vector<int> row_reduce(Matrix<T>& matrix, int pivot_col_limit, const T& eps,
bool reduced) {
std::vector<int> pivot_columns;
int pivot_row = 0;
for (int col = 0; col < pivot_col_limit && pivot_row < matrix.rows(); col++) {
int pivot = choose_pivot(matrix, pivot_row, col, eps);
if (pivot == -1) continue;
matrix.swap_rows(pivot_row, pivot);
const T pivot_value = matrix[pivot_row][col];
if (reduced) {
for (int j = col; j < matrix.cols(); j++) matrix[pivot_row][j] /= pivot_value;
}
const int first_row = reduced ? 0 : pivot_row + 1;
for (int row = first_row; row < matrix.rows(); row++) {
if (row == pivot_row || is_zero(matrix[row][col], eps)) continue;
T factor = matrix[row][col];
if (!reduced) factor /= pivot_value;
matrix[row][col] = T();
for (int j = col + 1; j < matrix.cols(); j++) {
matrix[row][j] -= factor * matrix[pivot_row][j];
}
}
pivot_columns.push_back(col);
pivot_row++;
}
if constexpr (std::is_floating_point_v<T>) {
for (T& value : matrix.data()) {
if (is_zero(value, eps)) value = T();
}
}
return pivot_columns;
}
} // namespace detail
template <class T>
struct RowReduction {
Matrix<T> matrix;
std::vector<int> pivot_columns;
int rank() const {
return int(pivot_columns.size());
}
};
template <class T>
RowReduction<T> reduced_row_echelon_form(Matrix<T> matrix,
T eps = default_epsilon<T>()) {
RowReduction<T> result;
result.pivot_columns = detail::row_reduce(matrix, matrix.cols(), eps, true);
result.matrix = std::move(matrix);
return result;
}
template <class T>
int matrix_rank(Matrix<T> matrix, T eps = default_epsilon<T>()) {
return int(detail::row_reduce(matrix, matrix.cols(), eps, false).size());
}
template <class T>
T determinant(Matrix<T> matrix, T eps = default_epsilon<T>()) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
T result = T(1);
bool negate = false;
for (int col = 0; col < size; col++) {
int pivot = detail::choose_pivot(matrix, col, col, eps);
if (pivot == -1) return T();
if (pivot != col) {
matrix.swap_rows(pivot, col);
negate = !negate;
}
const T pivot_value = matrix[col][col];
result *= pivot_value;
for (int row = col + 1; row < size; row++) {
if (detail::is_zero(matrix[row][col], eps)) continue;
const T factor = matrix[row][col] / pivot_value;
matrix[row][col] = T();
for (int j = col + 1; j < size; j++) {
matrix[row][j] -= factor * matrix[col][j];
}
}
}
return negate ? T() - result : result;
}
template <class T>
std::optional<Matrix<T>> inverse(const Matrix<T>& matrix,
T eps = default_epsilon<T>()) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
Matrix<T> augmented(size, size * 2);
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
augmented[row][col] = matrix[row][col];
}
augmented[row][size + row] = T(1);
}
const std::vector<int> pivots = detail::row_reduce(augmented, size, eps, true);
if (int(pivots.size()) != size) return std::nullopt;
Matrix<T> result(size, size);
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
result[row][col] = augmented[row][size + col];
}
}
return result;
}
template <class T>
struct LinearSystemResult {
bool consistent = false;
std::vector<T> particular_solution;
std::vector<std::vector<T>> nullspace_basis;
std::vector<int> pivot_columns;
int rank() const {
return int(pivot_columns.size());
}
int nullity() const {
return consistent ? int(nullspace_basis.size()) : 0;
}
bool has_unique_solution() const {
return consistent && nullspace_basis.empty();
}
};
template <class T>
LinearSystemResult<T> solve_linear_system(const Matrix<T>& coefficients,
const std::vector<T>& constants,
T eps = default_epsilon<T>()) {
assert(coefficients.rows() == int(constants.size()));
const int equation_count = coefficients.rows();
const int variable_count = coefficients.cols();
Matrix<T> augmented(equation_count, variable_count + 1);
for (int row = 0; row < equation_count; row++) {
for (int col = 0; col < variable_count; col++) {
augmented[row][col] = coefficients[row][col];
}
augmented[row][variable_count] = constants[std::size_t(row)];
}
LinearSystemResult<T> result;
result.pivot_columns =
detail::row_reduce(augmented, variable_count, eps, true);
for (int row = result.rank(); row < equation_count; row++) {
bool zero_left = true;
for (int col = 0; col < variable_count; col++) {
if (!detail::is_zero(augmented[row][col], eps)) {
zero_left = false;
break;
}
}
if (zero_left && !detail::is_zero(augmented[row][variable_count], eps)) {
return result;
}
}
result.consistent = true;
result.particular_solution.assign(std::size_t(variable_count), T());
std::vector<bool> is_pivot(std::size_t(variable_count), false);
for (int row = 0; row < result.rank(); row++) {
const int col = result.pivot_columns[std::size_t(row)];
is_pivot[std::size_t(col)] = true;
result.particular_solution[std::size_t(col)] = augmented[row][variable_count];
}
for (int free_col = 0; free_col < variable_count; free_col++) {
if (is_pivot[std::size_t(free_col)]) continue;
std::vector<T> direction(static_cast<std::size_t>(variable_count));
direction[std::size_t(free_col)] = T(1);
for (int row = 0; row < result.rank(); row++) {
const int pivot_col = result.pivot_columns[std::size_t(row)];
direction[std::size_t(pivot_col)] = T() - augmented[row][free_col];
}
result.nullspace_basis.push_back(std::move(direction));
}
return result;
}
} // namespace matrix
} // namespace m1une
#line 10 "graph/matrix_tree_theorem.hpp"
namespace m1une {
namespace graph {
namespace matrix_tree_detail {
inline int minor_index(int vertex, int removed) {
assert(vertex != removed);
return vertex < removed ? vertex : vertex - 1;
}
template <class Weight>
void assert_edge_incidence(const Graph<Weight>& graph, int expected) {
#ifndef NDEBUG
std::vector<int> incidence(graph.edge_count(), 0);
for (int vertex = 0; vertex < graph.size(); vertex++) {
for (const Edge<Weight>& edge : graph[vertex]) {
if (!edge.alive) continue;
assert(0 <= edge.id && edge.id < graph.edge_count());
incidence[edge.id]++;
}
}
for (int count : incidence) {
if (count != 0) assert(count == expected);
}
#else
(void)graph;
(void)expected;
#endif
}
template <class Field, class Weight>
Field count_arborescences(
const Graph<Weight>& graph,
int root,
bool outward
) {
const int n = graph.size();
assert(0 <= root && root < n);
assert_edge_incidence(graph, 1);
matrix::Matrix<Field> minor(n - 1, n - 1);
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<Weight>& edge : graph[vertex]) {
if (!edge.alive || edge.from == edge.to) continue;
const int row = outward ? edge.to : edge.from;
const int col = outward ? edge.from : edge.to;
if (row == root) continue;
const Field weight(edge.cost);
const int reduced_row = minor_index(row, root);
minor[reduced_row][reduced_row] += weight;
if (col != root) {
minor[reduced_row][minor_index(col, root)] -= weight;
}
}
}
return matrix::determinant(std::move(minor));
}
} // namespace matrix_tree_detail
// Returns the total weight of all undirected spanning trees. The weight of a
// tree is the product of its edge costs.
template <class Field, class Weight>
Field count_spanning_trees(const Graph<Weight>& graph) {
const int n = graph.size();
assert(n > 0);
matrix_tree_detail::assert_edge_incidence(graph, 2);
const int removed = n - 1;
matrix::Matrix<Field> minor(n - 1, n - 1);
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<Weight>& edge : graph[vertex]) {
if (!edge.alive || edge.from >= edge.to) continue;
const int from = edge.from;
const int to = edge.to;
const Field weight(edge.cost);
if (from != removed) {
const int reduced_from = matrix_tree_detail::minor_index(from, removed);
minor[reduced_from][reduced_from] += weight;
}
if (to != removed) {
const int reduced_to = matrix_tree_detail::minor_index(to, removed);
minor[reduced_to][reduced_to] += weight;
}
if (from != removed && to != removed) {
const int reduced_from = matrix_tree_detail::minor_index(from, removed);
const int reduced_to = matrix_tree_detail::minor_index(to, removed);
minor[reduced_from][reduced_to] -= weight;
minor[reduced_to][reduced_from] -= weight;
}
}
}
return matrix::determinant(std::move(minor));
}
// Counts directed spanning trees whose edges point away from root, so every
// vertex is reachable from root.
template <class Field, class Weight>
Field count_out_arborescences(const Graph<Weight>& graph, int root) {
return matrix_tree_detail::count_arborescences<Field>(graph, root, true);
}
// Counts directed spanning trees whose edges point toward root, so root is
// reachable from every vertex.
template <class Field, class Weight>
Field count_in_arborescences(const Graph<Weight>& graph, int root) {
return matrix_tree_detail::count_arborescences<Field>(graph, root, false);
}
} // namespace graph
} // namespace m1une
#line 1 "graph/scc.hpp"
#line 9 "graph/scc.hpp"
#line 11 "graph/scc.hpp"
namespace m1une {
namespace graph {
struct SccResult {
int count;
std::vector<int> comp;
std::vector<std::vector<int>> groups;
bool same(int u, int v) const {
assert(0 <= u && u < int(comp.size()));
assert(0 <= v && v < int(comp.size()));
return comp[u] == comp[v];
}
template <class T>
Graph<int> dag(const Graph<T>& g) const {
std::vector<std::pair<int, int>> edges;
for (int v = 0; v < g.size(); v++) {
for (const auto& e : g[v]) {
if (!e.alive) continue;
int a = comp[e.from], b = comp[e.to];
if (a != b) edges.emplace_back(a, b);
}
}
std::sort(edges.begin(), edges.end());
edges.erase(std::unique(edges.begin(), edges.end()), edges.end());
Graph<int> result(count);
for (auto [a, b] : edges) result.add_directed_edge(a, b);
return result;
}
};
template <class T>
SccResult strongly_connected_components(const Graph<T>& g) {
const int n = g.size();
std::vector<std::vector<int>> reverse_graph(n);
for (int vertex = 0; vertex < n; vertex++) {
for (const auto& edge : g[vertex]) {
if (edge.alive) reverse_graph[edge.to].push_back(vertex);
}
}
std::vector<char> seen(n, false);
std::vector<int> order;
order.reserve(n);
std::vector<std::pair<int, std::size_t>> dfs_stack;
for (int start = 0; start < n; start++) {
if (seen[start]) continue;
seen[start] = true;
dfs_stack.emplace_back(start, 0);
while (!dfs_stack.empty()) {
int vertex = dfs_stack.back().first;
std::size_t& edge_index = dfs_stack.back().second;
while (edge_index < g[vertex].size() &&
!g[vertex][edge_index].alive) {
edge_index++;
}
if (edge_index == g[vertex].size()) {
order.push_back(vertex);
dfs_stack.pop_back();
continue;
}
const int to = g[vertex][edge_index++].to;
if (!seen[to]) {
seen[to] = true;
dfs_stack.emplace_back(to, 0);
}
}
}
std::vector<int> comp(n, -1);
std::vector<std::vector<int>> groups;
std::vector<int> stack;
for (auto iterator = order.rbegin(); iterator != order.rend(); ++iterator) {
const int start = *iterator;
if (comp[start] != -1) continue;
const int component = int(groups.size());
groups.emplace_back();
comp[start] = component;
stack.push_back(start);
while (!stack.empty()) {
const int vertex = stack.back();
stack.pop_back();
groups.back().push_back(vertex);
for (int to : reverse_graph[vertex]) {
if (comp[to] != -1) continue;
comp[to] = component;
stack.push_back(to);
}
}
}
return SccResult{int(groups.size()), std::move(comp), std::move(groups)};
}
} // namespace graph
} // namespace m1une
#line 1 "graph/shortest_path.hpp"
#line 1 "graph/bellman_ford.hpp"
#line 9 "graph/bellman_ford.hpp"
#line 11 "graph/bellman_ford.hpp"
namespace m1une {
namespace graph {
template <class T>
struct BellmanFordResult {
std::vector<T> dist;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<bool> negative;
T inf;
bool has_negative_cycle;
bool reachable(int v) const {
assert(0 <= v && v < int(dist.size()));
return dist[v] != inf;
}
bool affected_by_negative_cycle(int v) const {
assert(0 <= v && v < int(negative.size()));
return negative[v];
}
std::vector<int> path(int t) const {
assert(reachable(t));
assert(!affected_by_negative_cycle(t));
std::vector<int> result;
for (int v = t; v != -1; v = parent[v]) result.push_back(v);
std::reverse(result.begin(), result.end());
return result;
}
};
template <class T>
BellmanFordResult<T> bellman_ford(const Graph<T>& g, const std::vector<int>& sources,
T inf = std::numeric_limits<T>::max() / T(4)) {
int n = g.size();
BellmanFordResult<T> result;
result.dist.assign(n, inf);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.negative.assign(n, false);
result.inf = inf;
result.has_negative_cycle = false;
for (int s : sources) {
assert(0 <= s && s < n);
result.dist[s] = T(0);
}
std::vector<int> relaxed_vertices;
for (int iter = 0; iter < n; iter++) {
bool updated = false;
for (int v = 0; v < n; v++) {
if (result.dist[v] == inf) continue;
for (const auto& e : g[v]) {
if (!e.alive) continue;
T nd = result.dist[v] + e.cost;
if (result.dist[e.to] <= nd) continue;
result.dist[e.to] = nd;
result.parent[e.to] = v;
result.parent_edge[e.to] = e.id;
updated = true;
if (iter == n - 1) relaxed_vertices.push_back(e.to);
}
}
if (!updated) break;
}
std::queue<int> que;
for (int v : relaxed_vertices) {
if (result.negative[v]) continue;
result.negative[v] = true;
que.push(v);
}
while (!que.empty()) {
int v = que.front();
que.pop();
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (result.negative[e.to]) continue;
result.negative[e.to] = true;
que.push(e.to);
}
}
for (bool x : result.negative) result.has_negative_cycle = result.has_negative_cycle || x;
return result;
}
template <class T>
BellmanFordResult<T> bellman_ford(const Graph<T>& g, int s, T inf = std::numeric_limits<T>::max() / T(4)) {
return bellman_ford(g, std::vector<int>{s}, inf);
}
} // namespace graph
} // namespace m1une
#line 1 "graph/bfs.hpp"
#line 11 "graph/bfs.hpp"
#line 13 "graph/bfs.hpp"
namespace m1une {
namespace graph {
struct BfsResult {
std::vector<int> dist;
std::vector<int> parent;
std::vector<int> parent_edge;
bool reachable(int v) const {
assert(0 <= v && v < int(dist.size()));
return dist[v] != -1;
}
std::vector<int> path(int t) const {
assert(reachable(t));
std::vector<int> result;
for (int v = t; v != -1; v = parent[v]) result.push_back(v);
std::reverse(result.begin(), result.end());
return result;
}
};
namespace bfs_detail {
template <class Callback>
concept BfsCallback =
std::invocable<Callback&, int, int> ||
std::invocable<Callback&, int>;
template <BfsCallback Callback>
void invoke_callback(Callback& callback, int vertex, int parent) {
if constexpr (std::invocable<Callback&, int, int>) {
std::invoke(callback, vertex, parent);
} else {
std::invoke(callback, vertex);
}
}
template <class T, class Callback>
BfsResult run_bfs(
const Graph<T>& g,
const std::vector<int>& sources,
Callback& callback
) {
int n = g.size();
BfsResult result;
result.dist.assign(n, -1);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
std::queue<int> que;
for (int s : sources) {
assert(0 <= s && s < n);
if (result.dist[s] != -1) continue;
result.dist[s] = 0;
invoke_callback(callback, s, -1);
que.push(s);
}
while (!que.empty()) {
int v = que.front();
que.pop();
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (result.dist[e.to] != -1) continue;
result.dist[e.to] = result.dist[v] + 1;
result.parent[e.to] = v;
result.parent_edge[e.to] = e.id;
invoke_callback(callback, e.to, v);
que.push(e.to);
}
}
return result;
}
} // namespace bfs_detail
template <class T>
BfsResult bfs(const Graph<T>& g, const std::vector<int>& sources) {
auto callback = [](int) {};
return bfs_detail::run_bfs(g, sources, callback);
}
template <class T>
BfsResult bfs(const Graph<T>& g, int s) {
return bfs(g, std::vector<int>{s});
}
template <class T, class Callback>
requires bfs_detail::BfsCallback<Callback>
BfsResult bfs(
const Graph<T>& g,
const std::vector<int>& sources,
Callback&& callback
) {
return bfs_detail::run_bfs(g, sources, callback);
}
template <class T, class Callback>
requires bfs_detail::BfsCallback<Callback>
BfsResult bfs(const Graph<T>& g, int source, Callback&& callback) {
return bfs(
g,
std::vector<int>{source},
std::forward<Callback>(callback)
);
}
} // namespace graph
} // namespace m1une
#line 1 "graph/cow_game.hpp"
#line 10 "graph/cow_game.hpp"
namespace m1une {
namespace graph {
template <class T>
struct CowGameConstraint {
int a;
int b;
T upper_bound;
};
template <class T>
struct CowGameSolution {
bool feasible = false;
std::vector<T> value;
bool is_feasible() const {
return feasible;
}
};
template <class T>
struct CowGameUpperBounds {
bool feasible;
std::vector<T> upper_bound;
T inf;
bool is_feasible() const {
return feasible;
}
bool bounded(int variable) const {
assert(0 <= variable && variable < int(upper_bound.size()));
return feasible && upper_bound[variable] != inf;
}
};
template <class T>
struct CowGameDifferenceBounds {
bool feasible;
std::optional<T> lower_bound;
std::optional<T> upper_bound;
bool is_feasible() const {
return feasible;
}
bool bounded_below() const {
return feasible && lower_bound.has_value();
}
bool bounded_above() const {
return feasible && upper_bound.has_value();
}
};
template <class T>
class CowGame {
static_assert(std::is_arithmetic_v<T> && std::is_signed_v<T>);
struct RelaxationResult {
bool has_negative_cycle;
std::vector<T> dist;
};
int _n;
std::vector<CowGameConstraint<T>> _constraints;
std::vector<std::vector<int>> _outgoing_constraints;
bool _has_negative_upper_bound = false;
mutable bool _solution_cached = false;
mutable CowGameSolution<T> _cached_solution;
void assert_variable(int variable) const {
(void)variable;
assert(0 <= variable && variable < _n);
}
T negate(T value) const {
assert(value != std::numeric_limits<T>::lowest());
return -value;
}
RelaxationResult check_feasibility() const {
std::vector<T> dist(_n, T());
for (int iteration = 0; iteration < _n; iteration++) {
bool updated = false;
for (const auto& constraint : _constraints) {
T candidate = dist[constraint.b] + constraint.upper_bound;
if (dist[constraint.a] <= candidate) continue;
dist[constraint.a] = candidate;
updated = true;
if (iteration == _n - 1) return RelaxationResult{true, std::move(dist)};
}
if (!updated) break;
}
return RelaxationResult{false, std::move(dist)};
}
std::vector<T> shortest_paths(int source, T inf) const {
const auto& potential = _cached_solution.value;
std::vector<T> dist(_n, inf);
std::vector<int> heap;
// -1 is unseen, -2 is fixed, and every other value is a heap index.
std::vector<int> position(_n, -1);
heap.reserve(_n);
auto swap_heap = [&](int i, int j) {
std::swap(heap[i], heap[j]);
position[heap[i]] = i;
position[heap[j]] = j;
};
auto sift_up = [&](int i) {
while (i > 0) {
int parent = (i - 1) / 2;
if (dist[heap[parent]] <= dist[heap[i]]) break;
swap_heap(parent, i);
i = parent;
}
};
auto sift_down = [&](int i) {
while (2 * i + 1 < int(heap.size())) {
int child = 2 * i + 1;
if (child + 1 < int(heap.size()) &&
dist[heap[child + 1]] < dist[heap[child]]) {
child++;
}
if (dist[heap[i]] <= dist[heap[child]]) break;
swap_heap(i, child);
i = child;
}
};
dist[source] = T();
position[source] = 0;
heap.push_back(source);
while (!heap.empty()) {
int b = heap[0];
position[b] = -2;
int last = heap.back();
heap.pop_back();
if (!heap.empty()) {
heap[0] = last;
position[last] = 0;
sift_down(0);
}
for (int id : _outgoing_constraints[b]) {
const auto& constraint = _constraints[id];
T cost = constraint.upper_bound + potential[b] -
potential[constraint.a];
assert(cost >= T());
T candidate = dist[b] + cost;
if (dist[constraint.a] <= candidate) continue;
dist[constraint.a] = candidate;
assert(position[constraint.a] != -2);
if (position[constraint.a] == -1) {
position[constraint.a] = int(heap.size());
heap.push_back(constraint.a);
}
sift_up(position[constraint.a]);
}
}
for (int v = 0; v < _n; v++) {
if (dist[v] == inf) continue;
dist[v] = dist[v] - potential[source] + potential[v];
}
return dist;
}
public:
CowGame() : CowGame(0) {}
explicit CowGame(int variable_count)
: _n(variable_count),
_outgoing_constraints(variable_count < 0 ? 0 : variable_count) {
assert(variable_count >= 0);
}
int size() const {
return _n;
}
int constraint_count() const {
return int(_constraints.size());
}
const CowGameConstraint<T>& get_constraint(int id) const {
assert(0 <= id && id < int(_constraints.size()));
return _constraints[id];
}
const std::vector<CowGameConstraint<T>>& constraints() const {
return _constraints;
}
bool can_use_dijkstra() const {
return !_has_negative_upper_bound ||
(_solution_cached && _cached_solution.feasible);
}
int add_upper_bound(int a, int b, T upper_bound) {
assert_variable(a);
assert_variable(b);
int id = int(_constraints.size());
_constraints.push_back(CowGameConstraint<T>{a, b, upper_bound});
_outgoing_constraints[b].push_back(id);
_has_negative_upper_bound = _has_negative_upper_bound || upper_bound < T();
_solution_cached = false;
return id;
}
int add_constraint(int a, int b, T upper_bound) {
return add_upper_bound(a, b, upper_bound);
}
int add_lower_bound(int a, int b, T lower_bound) {
return add_upper_bound(b, a, negate(lower_bound));
}
void add_bounds(int a, int b, T lower_bound, T upper_bound) {
assert(lower_bound <= upper_bound);
add_lower_bound(a, b, lower_bound);
add_upper_bound(a, b, upper_bound);
}
void add_equality(int a, int b, T difference) {
add_bounds(a, b, difference, difference);
}
CowGameSolution<T> solve() const {
if (_solution_cached) return _cached_solution;
_cached_solution.feasible = true;
_cached_solution.value.assign(_n, T());
if (_has_negative_upper_bound) {
auto result = check_feasibility();
_cached_solution.feasible = !result.has_negative_cycle;
_cached_solution.value.clear();
if (_cached_solution.feasible) {
_cached_solution.value = std::move(result.dist);
}
}
_solution_cached = true;
return _cached_solution;
}
bool is_feasible() const {
if (!_solution_cached) (void)solve();
return _cached_solution.feasible;
}
CowGameUpperBounds<T> tightest_upper_bounds(int source) const {
assert_variable(source);
T inf = std::numeric_limits<T>::max() / T(4);
CowGameUpperBounds<T> result;
result.feasible = is_feasible();
result.inf = inf;
result.upper_bound.assign(_n, inf);
if (!result.feasible) return result;
result.upper_bound = shortest_paths(source, inf);
return result;
}
CowGameDifferenceBounds<T> difference_bounds(int a, int b) const {
assert_variable(a);
assert_variable(b);
T inf = std::numeric_limits<T>::max() / T(4);
CowGameDifferenceBounds<T> result;
result.feasible = is_feasible();
if (!result.feasible) return result;
auto upper = shortest_paths(b, inf);
if (upper[a] != inf) result.upper_bound = upper[a];
auto lower = shortest_paths(a, inf);
if (lower[b] != inf) result.lower_bound = negate(lower[b]);
return result;
}
};
template <class T>
using DifferenceConstraints = CowGame<T>;
} // namespace graph
} // namespace m1une
#line 1 "graph/dijkstra.hpp"
#line 8 "graph/dijkstra.hpp"
#line 10 "graph/dijkstra.hpp"
namespace m1une {
namespace graph {
template <class T>
struct DijkstraResult {
std::vector<T> dist;
std::vector<char> reached;
std::vector<int> parent;
std::vector<int> parent_edge;
T inf = T();
bool reachable(int v) const {
assert(0 <= v && v < int(dist.size()));
return reached[v];
}
std::vector<int> path(int t) const {
assert(reachable(t));
std::vector<int> result;
for (int v = t; v != -1; v = parent[v]) result.push_back(v);
std::reverse(result.begin(), result.end());
return result;
}
};
namespace internal {
template <class T>
class DijkstraHeap {
private:
const std::vector<T>& dist_;
std::vector<int> heap_;
std::vector<int> position_;
bool less(int first, int second) const {
return dist_[heap_[first]] < dist_[heap_[second]];
}
void swap_nodes(int first, int second) {
std::swap(heap_[first], heap_[second]);
position_[heap_[first]] = first;
position_[heap_[second]] = second;
}
void sift_up(int index) {
while (index != 0) {
const int parent = (index - 1) / 2;
if (!less(index, parent)) break;
swap_nodes(index, parent);
index = parent;
}
}
void sift_down(int index) {
while (2 * index + 1 < int(heap_.size())) {
int child = 2 * index + 1;
if (child + 1 < int(heap_.size()) && less(child + 1, child)) {
++child;
}
if (!less(child, index)) break;
swap_nodes(index, child);
index = child;
}
}
public:
DijkstraHeap(const std::vector<T>& dist, int size)
: dist_(dist), position_(size, -1) {
heap_.reserve(size);
}
bool empty() const {
return heap_.empty();
}
void push_or_decrease(int vertex) {
int& position = position_[vertex];
if (position == -1) {
position = int(heap_.size());
heap_.push_back(vertex);
}
sift_up(position);
}
int pop_min() {
const int result = heap_.front();
position_[result] = -1;
if (heap_.size() == 1) {
heap_.pop_back();
return result;
}
heap_.front() = heap_.back();
position_[heap_.front()] = 0;
heap_.pop_back();
sift_down(0);
return result;
}
};
} // namespace internal
template <class T>
DijkstraResult<T> dijkstra(const Graph<T>& g,
const std::vector<int>& sources) {
int n = g.size();
DijkstraResult<T> result;
result.dist.resize(n);
result.reached.assign(n, false);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
internal::DijkstraHeap<T> que(result.dist, n);
for (int s : sources) {
assert(0 <= s && s < n);
if (result.reached[s]) continue;
result.reached[s] = true;
result.dist[s] = T();
que.push_or_decrease(s);
}
while (!que.empty()) {
const int current = que.pop_min();
for (const auto& e : g[current]) {
if (!e.alive) continue;
T nd = result.dist[current] + e.cost;
if (result.reached[e.to] && !(nd < result.dist[e.to])) continue;
result.reached[e.to] = true;
result.dist[e.to] = std::move(nd);
result.parent[e.to] = current;
result.parent_edge[e.to] = e.id;
que.push_or_decrease(e.to);
}
}
return result;
}
template <class T>
DijkstraResult<T> dijkstra(const Graph<T>& g, int s) {
return dijkstra(g, std::vector<int>{s});
}
// Compatibility overload: unreachable distances are replaced by inf after the
// search. Reachability itself never depends on this sentinel.
template <class T>
DijkstraResult<T> dijkstra(const Graph<T>& g,
const std::vector<int>& sources, const T& inf) {
DijkstraResult<T> result = dijkstra(g, sources);
result.inf = inf;
for (int v = 0; v < int(result.dist.size()); v++) {
if (!result.reachable(v)) result.dist[v] = inf;
}
return result;
}
template <class T>
DijkstraResult<T> dijkstra(const Graph<T>& g, int s, const T& inf) {
return dijkstra(g, std::vector<int>{s}, inf);
}
} // namespace graph
} // namespace m1une
#line 1 "graph/k_shortest_walk.hpp"
#line 10 "graph/k_shortest_walk.hpp"
#line 12 "graph/k_shortest_walk.hpp"
namespace m1une {
namespace graph {
namespace internal {
template <class T>
class KShortestWalkHeap {
struct Node {
T key;
int to;
int left;
int right;
int rank;
};
std::vector<Node> _nodes;
int rank(int root) const {
return root == -1 ? 0 : _nodes[root].rank;
}
public:
int make_node(T key, int to) {
int result = int(_nodes.size());
_nodes.push_back(Node{key, to, -1, -1, 1});
return result;
}
int meld_mutable(int first, int second) {
if (first == -1) return second;
if (second == -1) return first;
if (_nodes[second].key < _nodes[first].key) std::swap(first, second);
_nodes[first].right = meld_mutable(_nodes[first].right, second);
if (rank(_nodes[first].left) < rank(_nodes[first].right)) {
std::swap(_nodes[first].left, _nodes[first].right);
}
_nodes[first].rank = rank(_nodes[first].right) + 1;
return first;
}
int meld_persistent(int first, int second) {
if (first == -1) return second;
if (second == -1) return first;
if (_nodes[second].key < _nodes[first].key) std::swap(first, second);
int result = int(_nodes.size());
_nodes.push_back(_nodes[first]);
_nodes[result].right = meld_persistent(_nodes[result].right, second);
if (rank(_nodes[result].left) < rank(_nodes[result].right)) {
std::swap(_nodes[result].left, _nodes[result].right);
}
_nodes[result].rank = rank(_nodes[result].right) + 1;
return result;
}
const Node& operator[](int index) const {
return _nodes[index];
}
};
} // namespace internal
template <class T>
std::vector<T> k_shortest_walk(
const Graph<T>& g,
int s,
int t,
int k,
T inf = std::numeric_limits<T>::max() / T(4)
) {
int n = g.size();
assert(0 <= s && s < n);
assert(0 <= t && t < n);
assert(0 <= k);
if (k == 0) return {};
struct ReverseEdge {
int from;
int index;
T cost;
};
std::vector<std::vector<ReverseEdge>> reverse_graph(n);
for (int from = 0; from < n; from++) {
for (int index = 0; index < int(g[from].size()); index++) {
const auto& edge = g[from][index];
if (!edge.alive) continue;
assert(T(0) <= edge.cost);
reverse_graph[edge.to].push_back(ReverseEdge{from, index, edge.cost});
}
}
std::vector<T> dist(n, inf);
std::vector<int> tree_edge(n, -1);
std::vector<int> order;
order.reserve(n);
using QueueEntry = std::pair<T, int>;
std::priority_queue<QueueEntry, std::vector<QueueEntry>, std::greater<QueueEntry>> queue;
dist[t] = T(0);
queue.emplace(T(0), t);
while (!queue.empty()) {
auto [current_dist, vertex] = queue.top();
queue.pop();
if (dist[vertex] != current_dist) continue;
order.push_back(vertex);
for (const auto& edge : reverse_graph[vertex]) {
T next_dist = current_dist + edge.cost;
if (dist[edge.from] <= next_dist) continue;
dist[edge.from] = next_dist;
tree_edge[edge.from] = edge.index;
queue.emplace(next_dist, edge.from);
}
}
if (dist[s] == inf) return {};
internal::KShortestWalkHeap<T> heap_pool;
std::vector<int> local_heap(n, -1);
for (int vertex : order) {
for (int index = 0; index < int(g[vertex].size()); index++) {
const auto& edge = g[vertex][index];
if (!edge.alive || dist[edge.to] == inf || index == tree_edge[vertex]) continue;
T extra = edge.cost + dist[edge.to] - dist[vertex];
assert(T(0) <= extra);
int node = heap_pool.make_node(extra, edge.to);
local_heap[vertex] = heap_pool.meld_mutable(local_heap[vertex], node);
}
}
std::vector<int> path_heap(n, -1);
for (int vertex : order) {
int inherited = -1;
if (tree_edge[vertex] != -1) inherited = path_heap[g[vertex][tree_edge[vertex]].to];
path_heap[vertex] = heap_pool.meld_persistent(inherited, local_heap[vertex]);
}
std::vector<T> result;
result.reserve(k);
result.push_back(dist[s]);
std::priority_queue<QueueEntry, std::vector<QueueEntry>, std::greater<QueueEntry>> candidates;
if (path_heap[s] != -1) {
candidates.emplace(dist[s] + heap_pool[path_heap[s]].key, path_heap[s]);
}
while (int(result.size()) < k && !candidates.empty()) {
auto [cost, node_index] = candidates.top();
candidates.pop();
result.push_back(cost);
const auto& node = heap_pool[node_index];
if (node.left != -1) {
candidates.emplace(cost - node.key + heap_pool[node.left].key, node.left);
}
if (node.right != -1) {
candidates.emplace(cost - node.key + heap_pool[node.right].key, node.right);
}
int next_heap = path_heap[node.to];
if (next_heap != -1) {
candidates.emplace(cost + heap_pool[next_heap].key, next_heap);
}
}
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/warshall_floyd.hpp"
#line 8 "graph/warshall_floyd.hpp"
#line 10 "graph/warshall_floyd.hpp"
namespace m1une {
namespace graph {
template <class T>
std::vector<std::vector<T>> warshall_floyd(std::vector<std::vector<T>> dist,
T inf = std::numeric_limits<T>::max() / T(4)) {
int n = int(dist.size());
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
if (dist[i][k] == inf) continue;
for (int j = 0; j < n; j++) {
if (dist[k][j] == inf) continue;
T nd = dist[i][k] + dist[k][j];
if (nd < dist[i][j]) dist[i][j] = nd;
}
}
}
return dist;
}
template <class T>
std::vector<std::vector<T>> warshall_floyd(const Graph<T>& g, T inf = std::numeric_limits<T>::max() / T(4)) {
int n = g.size();
std::vector<std::vector<T>> dist(n, std::vector<T>(n, inf));
for (int i = 0; i < n; i++) dist[i][i] = T(0);
for (int v = 0; v < n; v++) {
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (e.cost < dist[e.from][e.to]) dist[e.from][e.to] = e.cost;
}
}
return warshall_floyd(std::move(dist), inf);
}
template <class T>
bool warshall_floyd_add_directed_edge(std::vector<std::vector<T>>& dist, int from, int to, T cost,
T inf = std::numeric_limits<T>::max() / T(4)) {
int n = int(dist.size());
assert(0 <= from && from < n);
assert(0 <= to && to < n);
std::vector<T> to_from(n), from_to(n);
for (int i = 0; i < n; i++) {
to_from[i] = dist[i][from];
from_to[i] = dist[to][i];
}
bool updated = false;
for (int i = 0; i < n; i++) {
if (to_from[i] == inf) continue;
for (int j = 0; j < n; j++) {
if (from_to[j] == inf) continue;
T nd = to_from[i] + cost + from_to[j];
if (nd < dist[i][j]) {
dist[i][j] = nd;
updated = true;
}
}
}
return updated;
}
template <class T>
bool warshall_floyd_add_undirected_edge(std::vector<std::vector<T>>& dist, int u, int v, T cost,
T inf = std::numeric_limits<T>::max() / T(4)) {
int n = int(dist.size());
assert(0 <= u && u < n);
assert(0 <= v && v < n);
std::vector<T> to_u(n), from_u(n), to_v(n), from_v(n);
for (int i = 0; i < n; i++) {
to_u[i] = dist[i][u];
from_u[i] = dist[u][i];
to_v[i] = dist[i][v];
from_v[i] = dist[v][i];
}
bool updated = false;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (to_u[i] != inf && from_v[j] != inf) {
T nd = to_u[i] + cost + from_v[j];
if (nd < dist[i][j]) {
dist[i][j] = nd;
updated = true;
}
}
if (to_v[i] != inf && from_u[j] != inf) {
T nd = to_v[i] + cost + from_u[j];
if (nd < dist[i][j]) {
dist[i][j] = nd;
updated = true;
}
}
}
}
return updated;
}
template <class T>
bool has_negative_cycle(const std::vector<std::vector<T>>& dist) {
int n = int(dist.size());
for (int i = 0; i < n; i++) {
if (dist[i][i] < T(0)) return true;
}
return false;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/zero_one_bfs.hpp"
#line 6 "graph/zero_one_bfs.hpp"
#include <deque>
#line 9 "graph/zero_one_bfs.hpp"
#line 11 "graph/zero_one_bfs.hpp"
namespace m1une {
namespace graph {
struct ZeroOneBfsResult {
std::vector<int> dist;
std::vector<int> parent;
std::vector<int> parent_edge;
int inf;
bool reachable(int v) const {
assert(0 <= v && v < int(dist.size()));
return dist[v] != inf;
}
std::vector<int> path(int t) const {
assert(reachable(t));
std::vector<int> result;
for (int v = t; v != -1; v = parent[v]) result.push_back(v);
std::reverse(result.begin(), result.end());
return result;
}
};
template <class T>
ZeroOneBfsResult zero_one_bfs(const Graph<T>& g, const std::vector<int>& sources,
int inf = std::numeric_limits<int>::max() / 2) {
int n = g.size();
ZeroOneBfsResult result;
result.dist.assign(n, inf);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.inf = inf;
std::deque<int> deq;
for (int s : sources) {
assert(0 <= s && s < n);
if (result.dist[s] == 0) continue;
result.dist[s] = 0;
deq.push_back(s);
}
while (!deq.empty()) {
int v = deq.front();
deq.pop_front();
for (const auto& e : g[v]) {
if (!e.alive) continue;
int w;
if (e.cost == T(0)) {
w = 0;
} else {
assert(e.cost == T(1));
w = 1;
}
int nd = result.dist[v] + w;
if (result.dist[e.to] <= nd) continue;
result.dist[e.to] = nd;
result.parent[e.to] = v;
result.parent_edge[e.to] = e.id;
if (w == 0) {
deq.push_front(e.to);
} else {
deq.push_back(e.to);
}
}
}
return result;
}
template <class T>
ZeroOneBfsResult zero_one_bfs(const Graph<T>& g, int s, int inf = std::numeric_limits<int>::max() / 2) {
return zero_one_bfs(g, std::vector<int>{s}, inf);
}
} // namespace graph
} // namespace m1une
#line 12 "graph/shortest_path.hpp"
#line 1 "graph/two_sat.hpp"
#line 9 "graph/two_sat.hpp"
namespace m1une {
namespace graph {
// A 2-SAT solver using iterative strongly connected components.
struct TwoSat {
private:
struct Csr {
std::vector<int> start;
std::vector<int> to;
};
int _n;
std::vector<std::pair<int, int>> _edges;
bool _solved;
bool _satisfiable;
std::vector<bool> _answer;
int node(int variable, bool value) const {
assert(0 <= variable && variable < _n);
return 2 * variable + int(value);
}
void add_edge(int from, int to) {
_edges.emplace_back(from, to);
_solved = false;
_answer.clear();
}
Csr build_csr(bool reverse) const {
int vertices = 2 * _n;
Csr graph;
graph.start.assign(vertices + 1, 0);
graph.to.resize(_edges.size());
for (auto [from, to] : _edges) {
int source = reverse ? to : from;
graph.start[source + 1]++;
}
for (int v = 0; v < vertices; v++) {
graph.start[v + 1] += graph.start[v];
}
std::vector<int> cursor = graph.start;
for (auto [from, to] : _edges) {
int source = reverse ? to : from;
int target = reverse ? from : to;
graph.to[cursor[source]++] = target;
}
return graph;
}
public:
TwoSat() : TwoSat(0) {}
explicit TwoSat(int n)
: _n(n), _solved(false), _satisfiable(false) {
assert(0 <= n);
assert(n <= std::numeric_limits<int>::max() / 2);
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
// Reserves space for approximately `clause_count` two-literal clauses.
void reserve(std::size_t clause_count) {
assert(clause_count <= std::size_t(std::numeric_limits<int>::max()) / 2);
_edges.reserve(2 * clause_count);
}
// Adds (variable i == f) OR (variable j == g).
void add_clause(int i, bool f, int j, bool g) {
int a = node(i, f);
int b = node(j, g);
add_edge(a ^ 1, b);
add_edge(b ^ 1, a);
}
// Adds (variable i == f) => (variable j == g).
void add_implication(int i, bool f, int j, bool g) {
add_clause(i, !f, j, g);
}
// Forces variable i to equal value.
void set_value(int i, bool value) {
add_clause(i, value, i, value);
}
// Forces variables i and j to have equal values.
void add_equal(int i, int j) {
add_clause(i, false, j, true);
add_clause(i, true, j, false);
}
// Forces variables i and j to have different values.
void add_not_equal(int i, int j) {
add_clause(i, true, j, true);
add_clause(i, false, j, false);
}
bool satisfiable() {
if (_solved) return _satisfiable;
assert(_edges.size() <= std::size_t(std::numeric_limits<int>::max()));
int vertices = 2 * _n;
Csr graph = build_csr(false);
Csr reverse_graph = build_csr(true);
std::vector<char> seen(vertices, false);
std::vector<int> order;
order.reserve(vertices);
std::vector<std::pair<int, int>> stack;
stack.reserve(vertices);
for (int start = 0; start < vertices; start++) {
if (seen[start]) continue;
seen[start] = true;
stack.emplace_back(start, graph.start[start]);
while (!stack.empty()) {
int v = stack.back().first;
int& edge = stack.back().second;
if (edge == graph.start[v + 1]) {
order.push_back(v);
stack.pop_back();
continue;
}
int to = graph.to[edge++];
if (!seen[to]) {
seen[to] = true;
stack.emplace_back(to, graph.start[to]);
}
}
}
std::vector<int> component(vertices, -1);
std::vector<int> vertices_stack;
vertices_stack.reserve(vertices);
int component_count = 0;
for (int index = vertices - 1; index >= 0; index--) {
int start = order[index];
if (component[start] != -1) continue;
component[start] = component_count;
vertices_stack.push_back(start);
while (!vertices_stack.empty()) {
int v = vertices_stack.back();
vertices_stack.pop_back();
for (int edge = reverse_graph.start[v];
edge < reverse_graph.start[v + 1];
edge++) {
int to = reverse_graph.to[edge];
if (component[to] == -1) {
component[to] = component_count;
vertices_stack.push_back(to);
}
}
}
component_count++;
}
_answer.assign(_n, false);
_satisfiable = true;
for (int i = 0; i < _n; i++) {
if (component[2 * i] == component[2 * i + 1]) {
_satisfiable = false;
_answer.clear();
break;
}
_answer[i] = component[2 * i] < component[2 * i + 1];
}
_solved = true;
return _satisfiable;
}
const std::vector<bool>& answer() const {
assert(_solved && _satisfiable);
return _answer;
}
bool value(int variable) const {
assert(_solved && _satisfiable);
assert(0 <= variable && variable < _n);
return _answer[variable];
}
};
} // namespace graph
} // namespace m1une
#line 16 "graph/directed.hpp"
#line 1 "graph/dominator_tree.hpp"
#line 7 "graph/dominator_tree.hpp"
#line 9 "graph/dominator_tree.hpp"
namespace m1une {
namespace graph {
struct DominatorTree {
int root;
std::vector<int> immediate_dominator;
std::vector<std::vector<int>> children;
std::vector<int> dfs_order;
std::vector<int> tin;
std::vector<int> tout;
int size() const {
return int(immediate_dominator.size());
}
bool reachable(int vertex) const {
assert(0 <= vertex && vertex < size());
return immediate_dominator[vertex] != -1;
}
bool dominates(int ancestor, int vertex) const {
assert(0 <= ancestor && ancestor < size());
assert(0 <= vertex && vertex < size());
return
reachable(ancestor) &&
reachable(vertex) &&
tin[ancestor] <= tin[vertex] &&
tin[vertex] < tout[ancestor];
}
};
// Lengauer-Tarjan immediate dominators from one start vertex.
template <class T>
DominatorTree dominator_tree(const Graph<T>& graph, int root) {
int n = graph.size();
assert(0 <= root && root < n);
std::vector<int> dfs_index(n, -1);
std::vector<int> vertex;
std::vector<int> parent_vertex(n, -1);
std::vector<std::pair<int, int>> stack;
dfs_index[root] = 0;
vertex.push_back(root);
stack.emplace_back(root, 0);
while (!stack.empty()) {
int current = stack.back().first;
int& edge_index = stack.back().second;
if (edge_index == int(graph[current].size())) {
stack.pop_back();
continue;
}
const auto& edge = graph[current][edge_index++];
if (!edge.alive || dfs_index[edge.to] != -1) continue;
parent_vertex[edge.to] = current;
dfs_index[edge.to] = int(vertex.size());
vertex.push_back(edge.to);
stack.emplace_back(edge.to, 0);
}
int reachable_count = int(vertex.size());
std::vector<std::vector<int>> predecessor(reachable_count);
for (int from : vertex) {
for (const auto& edge : graph[from]) {
if (!edge.alive || dfs_index[edge.to] == -1) continue;
predecessor[dfs_index[edge.to]].push_back(dfs_index[from]);
}
}
std::vector<int> parent(reachable_count, -1);
for (int index = 1; index < reachable_count; ++index) {
parent[index] = dfs_index[parent_vertex[vertex[index]]];
}
std::vector<int> semi(reachable_count);
std::vector<int> idom(reachable_count, -1);
std::vector<int> ancestor(reachable_count, -1);
std::vector<int> label(reachable_count);
std::vector<std::vector<int>> bucket(reachable_count);
for (int index = 0; index < reachable_count; ++index) {
semi[index] = index;
label[index] = index;
}
auto compress = [&](int start) {
std::vector<int> path;
int current = start;
while (
ancestor[current] != -1 &&
ancestor[ancestor[current]] != -1
) {
path.push_back(current);
current = ancestor[current];
}
for (int index = int(path.size()) - 1; index >= 0; --index) {
int node = path[index];
int parent_node = ancestor[node];
if (semi[label[parent_node]] < semi[label[node]]) {
label[node] = label[parent_node];
}
ancestor[node] = ancestor[parent_node];
}
};
auto eval = [&](int node) {
if (ancestor[node] == -1) return label[node];
compress(node);
int parent_node = ancestor[node];
if (semi[label[parent_node]] < semi[label[node]]) {
return label[parent_node];
}
return label[node];
};
for (int current = reachable_count - 1; current >= 1; --current) {
for (int previous : predecessor[current]) {
semi[current] = std::min(semi[current], semi[eval(previous)]);
}
bucket[semi[current]].push_back(current);
ancestor[current] = parent[current];
int parent_node = parent[current];
for (int node : bucket[parent_node]) {
int best = eval(node);
idom[node] =
semi[best] < semi[node] ? best : parent_node;
}
bucket[parent_node].clear();
}
for (int current = 1; current < reachable_count; ++current) {
if (idom[current] != semi[current]) {
idom[current] = idom[idom[current]];
}
}
idom[0] = 0;
DominatorTree result;
result.root = root;
result.immediate_dominator.assign(n, -1);
result.children.assign(n, {});
result.dfs_order = vertex;
for (int index = 0; index < reachable_count; ++index) {
int current = vertex[index];
int dominator = vertex[idom[index]];
result.immediate_dominator[current] = dominator;
if (current != root) result.children[dominator].push_back(current);
}
result.tin.assign(n, -1);
result.tout.assign(n, -1);
int timer = 0;
std::vector<std::pair<int, int>> tree_stack;
tree_stack.emplace_back(root, 0);
result.tin[root] = timer++;
while (!tree_stack.empty()) {
int current = tree_stack.back().first;
int& child_index = tree_stack.back().second;
if (child_index == int(result.children[current].size())) {
result.tout[current] = timer;
tree_stack.pop_back();
continue;
}
int child = result.children[current][child_index++];
result.tin[child] = timer++;
tree_stack.emplace_back(child, 0);
}
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/flow/flow.hpp"
#line 1 "graph/flow/bounded_flow.hpp"
#line 7 "graph/flow/bounded_flow.hpp"
#line 1 "graph/flow/max_flow.hpp"
#line 9 "graph/flow/max_flow.hpp"
namespace m1une {
namespace flow {
template <class Cap>
struct MaxFlow {
struct Edge {
int from;
int to;
Cap cap;
Cap flow;
};
private:
struct InternalEdge {
int to;
int rev;
Cap cap;
};
struct Position {
int from;
int edge;
};
int _n;
std::vector<Position> _pos;
std::vector<std::vector<InternalEdge>> _g;
Cap highest_label_preflow_push(int s, int t) {
const int dead = 2 * _n;
const int unreachable = _n + 1;
std::vector<Cap> excess(_n, Cap(0));
std::vector<int> state(8 * std::size_t(_n) + 2);
int* height = state.data();
int* height_count = height + _n;
int* current = height_count + dead + 1;
int* queue = current + _n;
int* next = queue + _n;
int* bucket_head = next + _n;
std::vector<char> active(_n, false);
int highest = -1;
long long work = 0;
const long long arc_count =
2LL * static_cast<long long>(_pos.size());
const long long work_limit = std::max(1LL, 4 * arc_count + _n);
auto activate = [&](int v) {
if (v == s || v == t || active[v] || excess[v] == Cap(0) ||
height[v] >= dead) {
return;
}
active[v] = true;
next[v] = bucket_head[height[v]];
bucket_head[height[v]] = v;
highest = std::max(highest, height[v]);
};
auto rebuild_buckets = [&]() {
std::fill(bucket_head, bucket_head + dead + 1, -1);
std::fill(active.begin(), active.end(), false);
highest = -1;
for (int v = 0; v < _n; v++) activate(v);
};
auto global_relabel = [&]() {
std::fill(height, height + _n, unreachable);
std::fill(height_count, height_count + dead + 1, 0);
std::fill(current, current + _n, 0);
int head = 0;
int tail = 0;
height[t] = 0;
height[s] = _n;
queue[tail++] = t;
while (head != tail) {
int v = queue[head++];
for (const auto& e : _g[v]) {
if (e.to == s || height[e.to] != unreachable) continue;
const auto& reverse = _g[e.to][e.rev];
if (reverse.cap == Cap(0)) continue;
height[e.to] = height[v] + 1;
queue[tail++] = e.to;
}
}
for (int v = 0; v < _n; v++) height_count[height[v]]++;
rebuild_buckets();
work = 0;
};
auto gap = [&](int empty_height) {
for (int v = 0; v < _n; v++) {
if (v == s || v == t || height[v] <= empty_height ||
height[v] >= _n) {
continue;
}
height_count[height[v]]--;
height[v] = unreachable;
height_count[height[v]]++;
current[v] = 0;
}
rebuild_buckets();
};
auto relabel = [&](int v) -> bool {
int old_height = height[v];
int new_height = dead;
work += int(_g[v].size());
for (const auto& e : _g[v]) {
if (e.cap != Cap(0)) {
new_height = std::min(new_height, height[e.to] + 1);
}
}
height_count[old_height]--;
height[v] = std::min(new_height, dead);
height_count[height[v]]++;
current[v] = 0;
if (old_height < _n && height_count[old_height] == 0) {
gap(old_height);
return true;
}
return false;
};
auto push = [&](int v, InternalEdge& e) {
Cap sent = std::min(excess[v], e.cap);
bool was_zero = excess[e.to] == Cap(0);
e.cap -= sent;
_g[e.to][e.rev].cap += sent;
excess[v] -= sent;
excess[e.to] += sent;
if (was_zero) activate(e.to);
};
auto discharge = [&](int v) {
while (excess[v] != Cap(0) && height[v] < dead) {
if (current[v] == int(_g[v].size())) {
if (relabel(v)) return;
continue;
}
auto& e = _g[v][current[v]];
work++;
if (e.cap != Cap(0) && height[v] == height[e.to] + 1) {
push(v, e);
} else {
current[v]++;
}
}
activate(v);
};
for (auto& e : _g[s]) {
if (e.to == s || e.cap == Cap(0)) continue;
Cap sent = e.cap;
e.cap = Cap(0);
_g[e.to][e.rev].cap += sent;
excess[e.to] += sent;
}
global_relabel();
while (highest >= 0) {
if (bucket_head[highest] == -1) {
highest--;
continue;
}
int v = bucket_head[highest];
bucket_head[highest] = next[v];
if (!active[v] || height[v] != highest) continue;
active[v] = false;
discharge(v);
if (work >= work_limit) global_relabel();
}
return excess[t];
}
public:
MaxFlow() : MaxFlow(0) {}
explicit MaxFlow(int n) : _n(n), _g(n) {
assert(0 <= n);
}
int size() const {
return _n;
}
int edge_count() const {
return int(_pos.size());
}
void reserve_edges(int edge_count) {
assert(0 <= edge_count);
_pos.reserve(edge_count);
if (_n == 0 || edge_count == 0 ||
2 * std::size_t(edge_count) < std::size_t(_n)) {
return;
}
const std::size_t average_degree =
(3 * std::size_t(edge_count) + std::size_t(_n) - 1)
/ std::size_t(_n);
for (auto& edges : _g) edges.reserve(average_degree);
}
void reserve_edges(int edge_count, const std::vector<int>& degrees) {
assert(0 <= edge_count);
assert(int(degrees.size()) == _n);
_pos.reserve(edge_count);
for (int v = 0; v < _n; v++) {
assert(0 <= degrees[v]);
_g[v].reserve(degrees[v]);
}
}
int add_edge(int from, int to, Cap cap) {
assert(0 <= from && from < _n);
assert(0 <= to && to < _n);
assert(Cap(0) <= cap);
int id = int(_pos.size());
int from_id = int(_g[from].size());
int to_id = int(_g[to].size());
if (from == to) to_id++;
_pos.push_back(Position{from, from_id});
_g[from].push_back(InternalEdge{to, to_id, cap});
_g[to].push_back(InternalEdge{from, from_id, Cap(0)});
return id;
}
int add_undirected_edge(int first, int second, Cap cap) {
static_assert(std::numeric_limits<Cap>::is_signed);
assert(0 <= first && first < _n);
assert(0 <= second && second < _n);
assert(Cap(0) <= cap);
assert(cap <= std::numeric_limits<Cap>::max() / Cap(2));
int id = int(_pos.size());
int first_id = int(_g[first].size());
int second_id = int(_g[second].size());
if (first == second) second_id++;
_pos.push_back(Position{first, ~first_id});
_g[first].push_back(InternalEdge{second, second_id, cap});
_g[second].push_back(InternalEdge{first, first_id, cap});
return id;
}
Edge get_edge(int i) const {
assert(0 <= i && i < int(_pos.size()));
const auto& position = _pos[i];
int from = position.from;
bool undirected = position.edge < 0;
int idx = undirected ? ~position.edge : position.edge;
const auto& e = _g[from][idx];
const auto& re = _g[e.to][e.rev];
if (undirected) {
return Edge{
from,
e.to,
(e.cap + re.cap) / Cap(2),
(re.cap - e.cap) / Cap(2)
};
}
return Edge{from, e.to, e.cap + re.cap, re.cap};
}
std::vector<Edge> edges() const {
std::vector<Edge> result;
result.reserve(_pos.size());
for (int i = 0; i < int(_pos.size()); i++) result.push_back(get_edge(i));
return result;
}
void change_edge(int i, Cap new_cap, Cap new_flow) {
assert(0 <= i && i < int(_pos.size()));
assert(Cap(0) <= new_cap);
auto& position = _pos[i];
int from = position.from;
bool undirected = position.edge < 0;
int idx = undirected ? ~position.edge : position.edge;
auto& e = _g[from][idx];
auto& re = _g[e.to][e.rev];
if (undirected) {
assert(new_cap <= std::numeric_limits<Cap>::max() / Cap(2));
assert(-new_cap <= new_flow && new_flow <= new_cap);
e.cap = new_cap - new_flow;
re.cap = new_cap + new_flow;
} else {
assert(Cap(0) <= new_flow && new_flow <= new_cap);
e.cap = new_cap - new_flow;
re.cap = new_flow;
}
}
Cap max_flow(int s, int t) {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
return highest_label_preflow_push(s, t);
}
Cap max_flow_push_relabel(int s, int t) {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
return highest_label_preflow_push(s, t);
}
Cap max_flow_dinic(int s, int t) {
return max_flow(s, t, std::numeric_limits<Cap>::max());
}
Cap max_flow(int s, int t, Cap flow_limit) {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
std::vector<int> work(3 * std::size_t(_n));
int* level = work.data();
int* iter = level + _n;
int* queue = iter + _n;
auto bfs = [&]() -> bool {
std::fill(level, level + _n, -1);
int head = 0;
int tail = 0;
level[s] = 0;
queue[tail++] = s;
while (head != tail) {
int v = queue[head++];
for (const auto& e : _g[v]) {
if (level[e.to] != -1 || e.cap == Cap(0)) continue;
level[e.to] = level[v] + 1;
if (e.to == t) return true;
queue[tail++] = e.to;
}
}
return level[t] != -1;
};
auto dfs = [&](auto&& self, int v, Cap up) -> Cap {
if (v == s) return up;
Cap result = Cap(0);
const int current_level = level[v];
auto& edges = _g[v];
const int edge_count = int(edges.size());
for (int& i = iter[v]; i < edge_count; i++) {
auto& e = edges[i];
if (level[e.to] + 1 != current_level) continue;
auto& reverse = _g[e.to][e.rev];
if (reverse.cap == Cap(0)) continue;
Cap d = self(
self,
e.to,
std::min(up - result, reverse.cap)
);
if (d == Cap(0)) continue;
e.cap += d;
reverse.cap -= d;
result += d;
if (result == up) return result;
}
level[v] = _n;
return result;
};
Cap flow = 0;
while (flow < flow_limit && bfs()) {
std::fill(iter, iter + _n, 0);
flow += dfs(dfs, t, flow_limit - flow);
}
return flow;
}
std::vector<bool> min_cut(int s) const {
assert(0 <= s && s < _n);
std::vector<bool> visited(_n, false);
std::vector<int> queue(_n);
int head = 0;
int tail = 0;
visited[s] = true;
queue[tail++] = s;
while (head != tail) {
int v = queue[head++];
for (const auto& e : _g[v]) {
if (e.cap == Cap(0) || visited[e.to]) continue;
visited[e.to] = true;
queue[tail++] = e.to;
}
}
return visited;
}
};
} // namespace flow
} // namespace m1une
#line 9 "graph/flow/bounded_flow.hpp"
namespace m1une {
namespace flow {
template <class Cap>
struct BoundedFlow {
struct Edge {
int from;
int to;
Cap lower;
Cap upper;
};
struct ResultEdge {
int from;
int to;
Cap lower;
Cap upper;
Cap flow;
};
struct Result {
std::vector<ResultEdge> edges;
std::vector<Cap> balance;
ResultEdge get_edge(int i) const {
assert(0 <= i && i < int(edges.size()));
return edges[i];
}
Cap flow(int i) const {
assert(0 <= i && i < int(edges.size()));
return edges[i].flow;
}
};
private:
int _n;
std::vector<Edge> _edges;
std::vector<Cap> _balance;
public:
BoundedFlow() : BoundedFlow(0) {}
explicit BoundedFlow(int n) : _n(n), _balance(n, Cap(0)) {
assert(0 <= n);
}
int size() const {
return _n;
}
int edge_count() const {
return int(_edges.size());
}
int add_edge(int from, int to, Cap lower, Cap upper) {
assert(0 <= from && from < _n);
assert(0 <= to && to < _n);
assert(lower <= upper);
int id = int(_edges.size());
_edges.push_back(Edge{from, to, lower, upper});
return id;
}
Edge get_edge(int i) const {
assert(0 <= i && i < int(_edges.size()));
return _edges[i];
}
std::vector<Edge> edges() const {
return _edges;
}
void set_balance(int v, Cap b) {
assert(0 <= v && v < _n);
_balance[v] = b;
}
void add_balance(int v, Cap b) {
assert(0 <= v && v < _n);
_balance[v] += b;
}
void add_supply(int v, Cap supply) {
assert(Cap(0) <= supply);
add_balance(v, supply);
}
void add_demand(int v, Cap demand) {
assert(Cap(0) <= demand);
add_balance(v, -demand);
}
Cap balance(int v) const {
assert(0 <= v && v < _n);
return _balance[v];
}
const std::vector<Cap>& balances() const {
return _balance;
}
std::optional<Result> feasible_flow() const {
return feasible_flow(_balance);
}
std::optional<Result> feasible_flow(const std::vector<Cap>& balance) const {
assert(int(balance.size()) == _n);
int ss = _n, tt = _n + 1;
MaxFlow<Cap> mf(_n + 2);
std::vector<int> edge_ids;
edge_ids.reserve(_edges.size());
std::vector<Cap> need = balance;
for (const auto& e : _edges) {
edge_ids.push_back(mf.add_edge(e.from, e.to, e.upper - e.lower));
need[e.from] -= e.lower;
need[e.to] += e.lower;
}
Cap positive_sum = Cap(0), negative_sum = Cap(0);
for (int v = 0; v < _n; v++) {
if (need[v] > Cap(0)) {
positive_sum += need[v];
mf.add_edge(ss, v, need[v]);
} else if (need[v] < Cap(0)) {
negative_sum += -need[v];
mf.add_edge(v, tt, -need[v]);
}
}
if (positive_sum != negative_sum) return std::nullopt;
if (mf.max_flow(ss, tt) != positive_sum) return std::nullopt;
Result result;
result.balance = balance;
result.edges.reserve(_edges.size());
for (int i = 0; i < int(_edges.size()); i++) {
auto used = mf.get_edge(edge_ids[i]).flow;
const auto& e = _edges[i];
result.edges.push_back(ResultEdge{e.from, e.to, e.lower, e.upper, e.lower + used});
}
return result;
}
std::optional<Result> feasible_st_flow(int s, int t, Cap flow_value) const {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
std::vector<Cap> balance = _balance;
balance[s] += flow_value;
balance[t] -= flow_value;
return feasible_flow(balance);
}
};
template <class Cap>
using BFlow = BoundedFlow<Cap>;
} // namespace flow
} // namespace m1une
#line 1 "graph/flow/bounded_min_cost_flow.hpp"
#line 7 "graph/flow/bounded_min_cost_flow.hpp"
#include <cmath>
#line 14 "graph/flow/bounded_min_cost_flow.hpp"
namespace m1une {
namespace flow {
template <
class Cap,
class Cost,
class TotalCost = Cost,
std::size_t PivotLimitFactor = 8
>
struct BoundedMinCostFlow {
static_assert(std::numeric_limits<Cap>::is_integer);
static_assert(std::numeric_limits<Cap>::is_signed);
static_assert(std::numeric_limits<Cost>::is_specialized);
static_assert(std::numeric_limits<Cost>::is_signed);
struct Edge {
int from;
int to;
Cap lower;
Cap upper;
Cost cost;
};
struct ResultEdge {
int from;
int to;
Cap lower;
Cap upper;
Cap flow;
Cost cost;
};
struct Result {
std::vector<ResultEdge> edges;
std::vector<Cap> balance;
std::vector<Cost> potential;
TotalCost cost;
ResultEdge get_edge(int i) const {
assert(0 <= i && i < int(edges.size()));
return edges[i];
}
Cap flow(int i) const {
assert(0 <= i && i < int(edges.size()));
return edges[i].flow;
}
};
private:
struct NetworkEdge {
int to;
Cap cap;
Cost cost;
};
struct NetworkSimplexSolver {
enum class Status {
optimal,
infeasible,
pivot_limit_reached,
};
struct Parent {
int vertex;
int edge;
Cap up;
Cap down;
};
int n;
std::vector<NetworkEdge> edges;
std::vector<Cap> excess;
std::vector<Cost> potential;
std::size_t pivot_count = 0;
NetworkSimplexSolver(int vertex_count, const std::vector<Cap>& balance)
: n(vertex_count), excess(balance) {}
void reserve_edges(int edge_count) {
edges.reserve(2 * (edge_count + n));
}
int add_edge(int from, int to, Cap lower, Cap upper, Cost cost) {
int id = int(edges.size()) / 2;
edges.push_back(NetworkEdge{to, upper - lower, cost});
edges.push_back(NetworkEdge{from, Cap(0), -cost});
excess[from] -= lower;
excess[to] += lower;
return id;
}
Status solve(std::size_t pivot_limit) {
pivot_count = 0;
const int original_edge_count = int(edges.size());
potential.assign(n + 1, Cost(0));
Cost artificial_cost = Cost(1);
for (int edge = 0; edge < original_edge_count; edge += 2) {
artificial_cost += edges[edge].cost < Cost(0)
? -edges[edge].cost : edges[edge].cost;
}
std::vector<Parent> parent(n);
edges.reserve(original_edge_count + 2 * n);
for (int vertex = 0; vertex < n; vertex++) {
if (excess[vertex] >= Cap(0)) {
edges.push_back(NetworkEdge{n, Cap(0), artificial_cost});
edges.push_back(NetworkEdge{vertex, excess[vertex], -artificial_cost});
potential[vertex] = -artificial_cost;
} else {
edges.push_back(NetworkEdge{n, -excess[vertex], -artificial_cost});
edges.push_back(NetworkEdge{vertex, Cap(0), artificial_cost});
potential[vertex] = artificial_cost;
}
int edge = int(edges.size()) - 2;
parent[vertex] = Parent{
n, edge, edges[edge].cap, edges[edge ^ 1].cap
};
}
std::vector<int> depth(n + 1, 1);
depth[n] = 0;
std::vector<int> next(2 * (n + 1));
std::vector<int> previous(2 * (n + 1));
auto connect = [&](int first, int second) {
next[first] = second;
previous[second] = first;
};
for (int vertex = 0; vertex <= n; vertex++) {
connect(2 * vertex, 2 * vertex + 1);
}
for (int vertex = 0; vertex < n; vertex++) {
connect(2 * vertex + 1, next[2 * n]);
connect(2 * n, 2 * vertex);
}
auto push_flow = [&](int entering_edge) {
const int first = edges[entering_edge ^ 1].to;
const int second = edges[entering_edge].to;
const Cost cycle_cost =
edges[entering_edge].cost
+ potential[first] - potential[second];
Cap amount = edges[entering_edge].cap;
bool leave_first_side = true;
int leaving_vertex = second;
int first_ancestor = first;
int second_ancestor = second;
auto move_first_up = [&] {
if (parent[first_ancestor].down < amount) {
amount = parent[first_ancestor].down;
leaving_vertex = first_ancestor;
leave_first_side = true;
}
first_ancestor = parent[first_ancestor].vertex;
};
auto move_second_up = [&] {
if (parent[second_ancestor].up <= amount) {
amount = parent[second_ancestor].up;
leaving_vertex = second_ancestor;
leave_first_side = false;
}
second_ancestor = parent[second_ancestor].vertex;
};
if (depth[first_ancestor] >= depth[second_ancestor]) {
int difference = depth[first_ancestor] - depth[second_ancestor];
for (int i = 0; i < difference; i++) move_first_up();
} else {
int difference = depth[second_ancestor] - depth[first_ancestor];
for (int i = 0; i < difference; i++) move_second_up();
}
while (first_ancestor != second_ancestor) {
move_first_up();
move_second_up();
}
const int ancestor = first_ancestor;
if (amount != Cap(0)) {
int vertex = first;
while (vertex != ancestor) {
parent[vertex].up += amount;
parent[vertex].down -= amount;
vertex = parent[vertex].vertex;
}
vertex = second;
while (vertex != ancestor) {
parent[vertex].up -= amount;
parent[vertex].down += amount;
vertex = parent[vertex].vertex;
}
}
int vertex = first;
int new_parent = second;
std::pair<Cap, Cap> parent_capacities{
edges[entering_edge].cap - amount,
edges[entering_edge ^ 1].cap + amount
};
Cost potential_difference = -cycle_cost;
if (!leave_first_side) {
std::swap(vertex, new_parent);
std::swap(parent_capacities.first, parent_capacities.second);
potential_difference = -potential_difference;
}
int parent_edge = entering_edge ^ (leave_first_side ? 0 : 1);
while (new_parent != leaving_vertex) {
int new_depth = depth[new_parent];
int tour_index = 2 * vertex;
while (tour_index != 2 * vertex + 1) {
if ((tour_index & 1) == 0) {
new_depth++;
potential[tour_index / 2] += potential_difference;
depth[tour_index / 2] = new_depth;
} else {
new_depth--;
}
tour_index = next[tour_index];
}
connect(previous[2 * vertex], next[2 * vertex + 1]);
connect(2 * vertex + 1, next[2 * new_parent]);
connect(2 * new_parent, 2 * vertex);
std::swap(parent[vertex].edge, parent_edge);
parent_edge ^= 1;
std::swap(parent[vertex].up, parent_capacities.first);
std::swap(parent[vertex].down, parent_capacities.second);
std::swap(parent_capacities.first, parent_capacities.second);
int old_parent = parent[vertex].vertex;
parent[vertex].vertex = new_parent;
new_parent = vertex;
vertex = old_parent;
}
edges[parent_edge].cap = parent_capacities.first;
edges[parent_edge ^ 1].cap = parent_capacities.second;
};
bool pivot_limit_reached = false;
auto pivot = [&](int entering_edge) {
if (pivot_count == pivot_limit) {
pivot_limit_reached = true;
return false;
}
push_flow(entering_edge);
pivot_count++;
return true;
};
const int candidate_limit = std::max(
int(0.2 * std::sqrt(double(original_edge_count))), 10
);
const int minor_limit = std::max(candidate_limit / 10, 3);
std::vector<int> candidates;
candidates.reserve(candidate_limit);
auto minor_pivot = [&] {
Cost best_cost = Cost(0);
int best_edge = -1;
int index = 0;
while (index < int(candidates.size())) {
int edge = candidates[index];
if (edges[edge].cap == Cap(0)) {
candidates[index] = candidates.back();
candidates.pop_back();
continue;
}
Cost reduced_cost =
edges[edge].cost
+ potential[edges[edge ^ 1].to]
- potential[edges[edge].to];
if (reduced_cost >= Cost(0)) {
candidates[index] = candidates.back();
candidates.pop_back();
continue;
}
if (reduced_cost < best_cost) {
best_cost = reduced_cost;
best_edge = edge;
}
index++;
}
if (best_edge == -1) return false;
return pivot(best_edge);
};
int edge = 0;
while (true) {
for (int iteration = 0; iteration < minor_limit; iteration++) {
if (!minor_pivot()) break;
}
if (pivot_limit_reached) return Status::pivot_limit_reached;
Cost best_cost = Cost(0);
int best_edge = -1;
candidates.clear();
for (int scanned = 0; scanned < int(edges.size()); scanned++) {
if (edges[edge].cap != Cap(0)) {
Cost reduced_cost =
edges[edge].cost
+ potential[edges[edge ^ 1].to]
- potential[edges[edge].to];
if (reduced_cost < Cost(0)) {
if (reduced_cost < best_cost) {
best_cost = reduced_cost;
best_edge = edge;
}
candidates.push_back(edge);
if (int(candidates.size()) == candidate_limit) break;
}
}
edge++;
if (edge == int(edges.size())) edge = 0;
}
if (candidates.empty()) break;
if (!pivot(best_edge)) return Status::pivot_limit_reached;
}
for (int vertex = 0; vertex < n; vertex++) {
edges[parent[vertex].edge].cap = parent[vertex].up;
edges[parent[vertex].edge ^ 1].cap = parent[vertex].down;
}
bool feasible = true;
for (int vertex = 0; vertex < n; vertex++) {
int artificial_edge = original_edge_count + 2 * vertex;
if (
(excess[vertex] >= Cap(0)
&& edges[artificial_edge ^ 1].cap != Cap(0))
|| (excess[vertex] < Cap(0)
&& edges[artificial_edge].cap != Cap(0))
) {
feasible = false;
break;
}
}
potential.pop_back();
return feasible ? Status::optimal : Status::infeasible;
}
Cap edge_flow(int edge_id, Cap lower) const {
return lower + edges[2 * edge_id + 1].cap;
}
};
struct ScalingEdge {
int to;
int reverse;
Cap cap;
Cap flow;
Cost cost;
};
struct ScalingSolver {
int n;
std::vector<std::vector<ScalingEdge>> graph;
std::vector<std::pair<int, int>> positions;
std::vector<Cap> excess;
std::vector<Cost> potential;
std::vector<Cost> distance;
std::vector<int> parent_vertex;
std::vector<int> parent_edge;
std::vector<int> excess_vertices;
std::vector<int> deficit_vertices;
Cost farthest = Cost(0);
ScalingSolver(int vertex_count, const std::vector<Cap>& balance)
: n(vertex_count), graph(vertex_count), excess(balance),
potential(vertex_count, Cost(0)) {}
void reserve_edges(int edge_count) {
positions.reserve(edge_count);
}
int add_edge(int from, int to, Cap lower, Cap upper, Cost cost) {
int id = int(positions.size());
int from_edge = int(graph[from].size());
int to_edge = int(graph[to].size());
if (from == to) to_edge++;
positions.emplace_back(from, from_edge);
graph[from].push_back(ScalingEdge{
to, to_edge, upper, Cap(0), cost
});
graph[to].push_back(ScalingEdge{
from, from_edge, -lower, Cap(0), -cost
});
return id;
}
Cap residual_capacity(int from, int edge_id) const {
const auto& edge = graph[from][edge_id];
return edge.cap - edge.flow;
}
Cost residual_cost(int from, const ScalingEdge& edge) const {
return edge.cost + potential[from] - potential[edge.to];
}
void push(int from, int edge_id, Cap amount) {
auto& edge = graph[from][edge_id];
edge.flow += amount;
graph[edge.to][edge.reverse].flow -= amount;
}
void saturate_negative(Cap delta) {
excess_vertices.clear();
deficit_vertices.clear();
for (int from = 0; from < n; from++) {
for (
int edge_id = 0;
edge_id < int(graph[from].size());
edge_id++
) {
const auto& edge = graph[from][edge_id];
Cap residual = edge.cap - edge.flow;
residual -= residual % delta;
if (
residual_cost(from, edge) < Cost(0)
|| residual < Cap(0)
) {
int to = edge.to;
push(from, edge_id, residual);
excess[from] -= residual;
excess[to] += residual;
}
}
}
for (int vertex = 0; vertex < n; vertex++) {
if (excess[vertex] > Cap(0)) {
excess_vertices.push_back(vertex);
} else if (excess[vertex] < Cap(0)) {
deficit_vertices.push_back(vertex);
}
}
}
bool dual(Cap delta) {
excess_vertices.erase(
std::remove_if(
excess_vertices.begin(), excess_vertices.end(),
[&](int vertex) { return excess[vertex] < delta; }
),
excess_vertices.end()
);
deficit_vertices.erase(
std::remove_if(
deficit_vertices.begin(), deficit_vertices.end(),
[&](int vertex) { return excess[vertex] > -delta; }
),
deficit_vertices.end()
);
const Cost unreachable = std::numeric_limits<Cost>::max();
distance.assign(n, unreachable);
parent_vertex.assign(n, -1);
parent_edge.assign(n, -1);
using QueueEntry = std::pair<Cost, int>;
std::priority_queue<
QueueEntry,
std::vector<QueueEntry>,
std::greater<QueueEntry>
> queue;
for (int vertex : excess_vertices) {
distance[vertex] = Cost(0);
queue.emplace(Cost(0), vertex);
}
farthest = Cost(0);
int reached_deficits = 0;
while (!queue.empty()) {
auto [current_distance, from] = queue.top();
queue.pop();
if (distance[from] != current_distance) continue;
farthest = current_distance;
if (excess[from] <= -delta) reached_deficits++;
if (reached_deficits >= int(deficit_vertices.size())) break;
for (
int edge_id = 0;
edge_id < int(graph[from].size());
edge_id++
) {
const auto& edge = graph[from][edge_id];
if (edge.cap - edge.flow < delta) continue;
Cost next_distance =
current_distance + residual_cost(from, edge);
if (next_distance >= distance[edge.to]) continue;
distance[edge.to] = next_distance;
parent_vertex[edge.to] = from;
parent_edge[edge.to] = edge_id;
queue.emplace(next_distance, edge.to);
}
}
for (int vertex = 0; vertex < n; vertex++) {
potential[vertex] += std::min(distance[vertex], farthest);
}
return reached_deficits > 0;
}
void primal(Cap delta) {
for (int sink : deficit_vertices) {
if (distance[sink] > farthest) continue;
Cap amount = -excess[sink];
int root = sink;
while (parent_edge[root] != -1) {
int from = parent_vertex[root];
amount = std::min(
amount,
residual_capacity(from, parent_edge[root])
);
root = from;
}
amount = std::min(amount, excess[root]);
amount -= amount % delta;
if (amount <= Cap(0)) continue;
int vertex = sink;
while (parent_edge[vertex] != -1) {
int from = parent_vertex[vertex];
int edge_id = parent_edge[vertex];
push(from, edge_id, amount);
if (residual_capacity(from, edge_id) == Cap(0)) {
parent_edge[vertex] = -1;
}
vertex = from;
}
excess[sink] += amount;
excess[root] -= amount;
}
}
bool solve() {
Cap scale_bound = Cap(1);
for (Cap value : excess) {
scale_bound = std::max(scale_bound, value);
scale_bound = std::max(scale_bound, -value);
}
for (const auto& edges : graph) {
for (const auto& edge : edges) {
Cap residual = edge.cap - edge.flow;
scale_bound = std::max(scale_bound, residual);
scale_bound = std::max(scale_bound, -residual);
}
}
Cap delta = Cap(1);
while (delta <= scale_bound / Cap(2)) delta *= Cap(2);
while (true) {
saturate_negative(delta);
while (dual(delta)) primal(delta);
if (delta == Cap(1)) break;
delta /= Cap(2);
}
return excess_vertices.empty() && deficit_vertices.empty();
}
Cap edge_flow(int edge_id, Cap) const {
auto [from, index] = positions[edge_id];
return graph[from][index].flow;
}
};
int _n;
std::vector<Edge> _edges;
std::vector<Cap> _balance;
template <class Solver>
Result make_result(
const std::vector<Cap>& balance,
const Solver& solver,
std::vector<Cost> potential
) const {
Result result;
result.balance = balance;
result.cost = TotalCost(0);
result.edges.reserve(_edges.size());
for (int i = 0; i < int(_edges.size()); i++) {
const auto& edge = _edges[i];
Cap flow = solver.edge_flow(i, edge.lower);
result.cost += TotalCost(flow) * TotalCost(edge.cost);
result.edges.push_back(ResultEdge{
edge.from,
edge.to,
edge.lower,
edge.upper,
flow,
edge.cost
});
}
result.potential = std::move(potential);
return result;
}
std::vector<Cost> residual_potential(
const std::vector<ResultEdge>& edges
) const {
std::vector<Cost> potential(_n, Cost(0));
bool updated = false;
for (int iteration = 0; iteration < _n; iteration++) {
updated = false;
for (const ResultEdge& edge : edges) {
if (
edge.flow < edge.upper
&& potential[edge.to] > potential[edge.from] + edge.cost
) {
potential[edge.to] = potential[edge.from] + edge.cost;
updated = true;
}
if (
edge.lower < edge.flow
&& potential[edge.from] > potential[edge.to] - edge.cost
) {
potential[edge.from] = potential[edge.to] - edge.cost;
updated = true;
}
}
if (!updated) break;
}
assert(!updated);
return potential;
}
std::optional<Result> polynomial_min_cost_flow_impl(
const std::vector<Cap>& balance
) const {
ScalingSolver solver(_n, balance);
solver.reserve_edges(int(_edges.size()));
for (const auto& edge : _edges) {
solver.add_edge(
edge.from,
edge.to,
edge.lower,
edge.upper,
edge.cost
);
}
if (!solver.solve()) return std::nullopt;
Result result = make_result(balance, solver, {});
result.potential = residual_potential(result.edges);
return result;
}
public:
BoundedMinCostFlow() : BoundedMinCostFlow(0) {}
explicit BoundedMinCostFlow(int n) : _n(n), _balance(n, Cap(0)) {
assert(0 <= n);
}
int size() const {
return _n;
}
int edge_count() const {
return int(_edges.size());
}
void reserve_edges(int edge_count) {
assert(0 <= edge_count);
_edges.reserve(edge_count);
}
int add_edge(int from, int to, Cap lower, Cap upper, Cost cost) {
assert(0 <= from && from < _n);
assert(0 <= to && to < _n);
assert(lower <= upper);
int id = int(_edges.size());
_edges.push_back(Edge{from, to, lower, upper, cost});
return id;
}
Edge get_edge(int i) const {
assert(0 <= i && i < int(_edges.size()));
return _edges[i];
}
std::vector<Edge> edges() const {
return _edges;
}
void set_balance(int v, Cap b) {
assert(0 <= v && v < _n);
_balance[v] = b;
}
void add_balance(int v, Cap b) {
assert(0 <= v && v < _n);
_balance[v] += b;
}
void add_supply(int v, Cap supply) {
assert(Cap(0) <= supply);
add_balance(v, supply);
}
void add_demand(int v, Cap demand) {
assert(Cap(0) <= demand);
add_balance(v, -demand);
}
Cap balance(int v) const {
assert(0 <= v && v < _n);
return _balance[v];
}
const std::vector<Cap>& balances() const {
return _balance;
}
std::optional<Result> min_cost_flow() const {
return min_cost_flow(_balance);
}
std::optional<Result> min_cost_flow(const std::vector<Cap>& balance) const {
assert(int(balance.size()) == _n);
Cap balance_sum = Cap(0);
for (Cap value : balance) balance_sum += value;
if (balance_sum != Cap(0)) return std::nullopt;
NetworkSimplexSolver solver(_n, balance);
solver.reserve_edges(int(_edges.size()));
for (const auto& edge : _edges) {
solver.add_edge(edge.from, edge.to, edge.lower, edge.upper, edge.cost);
}
const std::size_t graph_size =
std::size_t(_n) + _edges.size() + 1;
std::size_t pivot_limit = 0;
if constexpr (PivotLimitFactor != 0) {
const std::size_t maximum =
std::numeric_limits<std::size_t>::max();
pivot_limit = graph_size > maximum / PivotLimitFactor
? maximum : PivotLimitFactor * graph_size;
}
auto status = solver.solve(pivot_limit);
if (status == NetworkSimplexSolver::Status::infeasible) {
return std::nullopt;
}
if (status == NetworkSimplexSolver::Status::pivot_limit_reached) {
return polynomial_min_cost_flow_impl(balance);
}
return make_result(balance, solver, std::move(solver.potential));
}
std::optional<Result> min_cost_flow_polynomial() const {
return min_cost_flow_polynomial(_balance);
}
std::optional<Result> min_cost_flow_polynomial(
const std::vector<Cap>& balance
) const {
assert(int(balance.size()) == _n);
Cap balance_sum = Cap(0);
for (Cap value : balance) balance_sum += value;
if (balance_sum != Cap(0)) return std::nullopt;
return polynomial_min_cost_flow_impl(balance);
}
std::optional<Result> min_cost_st_flow(int s, int t, Cap flow_value) const {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
std::vector<Cap> balance = _balance;
balance[s] += flow_value;
balance[t] -= flow_value;
return min_cost_flow(balance);
}
std::optional<Result> min_cost_st_flow_polynomial(
int s,
int t,
Cap flow_value
) const {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
std::vector<Cap> balance = _balance;
balance[s] += flow_value;
balance[t] -= flow_value;
return min_cost_flow_polynomial(balance);
}
};
template <
class Cap,
class Cost,
class TotalCost = Cost,
std::size_t PivotLimitFactor = 8
>
using BMinCostFlow = BoundedMinCostFlow<
Cap,
Cost,
TotalCost,
PivotLimitFactor
>;
} // namespace flow
} // namespace m1une
#line 1 "graph/flow/gomory_hu.hpp"
#line 9 "graph/flow/gomory_hu.hpp"
namespace m1une {
namespace flow {
template <class Cap>
struct GomoryHu {
struct Edge {
int u;
int v;
Cap cap;
};
private:
struct FlowEdge {
int to;
int rev;
Cap cap;
Cap initial_cap;
};
int _n;
bool _built = false;
std::vector<Edge> _edges;
std::vector<Edge> _tree_edges;
std::vector<int> _parent;
std::vector<Cap> _cut_value;
std::vector<std::vector<std::pair<int, Cap>>> _tree;
std::vector<std::vector<int>> _up;
std::vector<std::vector<Cap>> _minimum;
std::vector<int> _depth;
std::vector<std::vector<FlowEdge>> _graph;
std::vector<Cap> _excess;
std::vector<int> _height;
std::vector<int> _height_count;
std::vector<int> _current;
std::vector<bool> _active;
std::vector<std::vector<int>> _buckets;
std::vector<int> _queue;
int _highest;
long long _work;
long long _work_limit;
void add_flow_edge(int u, int v, Cap cap) {
if (u == v || cap == Cap(0)) return;
int ui = int(_graph[u].size());
int vi = int(_graph[v].size());
_graph[u].push_back(FlowEdge{v, vi, cap, cap});
_graph[v].push_back(FlowEdge{u, ui, cap, cap});
}
void reset_flow() {
for (auto& edges : _graph) {
for (auto& edge : edges) edge.cap = edge.initial_cap;
}
}
void activate(int v, int s, int t) {
int dead = 2 * _n;
if (v == s || v == t || _active[v] || _excess[v] == Cap(0) || _height[v] >= dead) return;
_active[v] = true;
_buckets[_height[v]].push_back(v);
_highest = std::max(_highest, _height[v]);
}
void rebuild_buckets(int s, int t) {
for (auto& bucket : _buckets) bucket.clear();
std::fill(_active.begin(), _active.end(), false);
_highest = -1;
for (int v = 0; v < _n; v++) activate(v, s, t);
}
void global_relabel(int s, int t) {
int dead = 2 * _n;
int unreachable = _n + 1;
std::fill(_height.begin(), _height.end(), unreachable);
std::fill(_height_count.begin(), _height_count.end(), 0);
std::fill(_current.begin(), _current.end(), 0);
int head = 0;
int tail = 0;
_height[t] = 0;
_height[s] = _n;
_queue[tail++] = t;
while (head < tail) {
int v = _queue[head++];
for (const auto& edge : _graph[v]) {
const FlowEdge& reverse = _graph[edge.to][edge.rev];
if (reverse.cap == Cap(0) || _height[edge.to] != unreachable) continue;
_height[edge.to] = _height[v] + 1;
_queue[tail++] = edge.to;
}
}
for (int v = 0; v < _n; v++) {
_height[v] = std::min(_height[v], dead);
_height_count[_height[v]]++;
}
rebuild_buckets(s, t);
_work = 0;
}
void push(int v, FlowEdge& edge, int s, int t) {
if (edge.cap == Cap(0) || _height[v] != _height[edge.to] + 1) return;
Cap sent = std::min(_excess[v], edge.cap);
if (sent == Cap(0)) return;
bool was_zero = _excess[edge.to] == Cap(0);
edge.cap -= sent;
_graph[edge.to][edge.rev].cap += sent;
_excess[v] -= sent;
_excess[edge.to] += sent;
if (was_zero) activate(edge.to, s, t);
}
void gap(int height, int s, int t) {
int unreachable = _n + 1;
for (int v = 0; v < _n; v++) {
if (v == s || v == t || _height[v] <= height || _height[v] >= _n) continue;
_height_count[_height[v]]--;
_height[v] = unreachable;
_height_count[_height[v]]++;
_current[v] = 0;
}
rebuild_buckets(s, t);
}
bool relabel(int v, int s, int t) {
int dead = 2 * _n;
int old_height = _height[v];
int new_height = dead;
_work += int(_graph[v].size());
for (const auto& edge : _graph[v]) {
if (edge.cap != Cap(0)) new_height = std::min(new_height, _height[edge.to] + 1);
}
_height_count[old_height]--;
_height[v] = std::min(new_height, dead);
_height_count[_height[v]]++;
_current[v] = 0;
if (old_height < _n && _height_count[old_height] == 0) {
gap(old_height, s, t);
return true;
}
return false;
}
void discharge(int v, int s, int t) {
while (_excess[v] != Cap(0) && _height[v] < 2 * _n) {
if (_current[v] == int(_graph[v].size())) {
if (relabel(v, s, t)) return;
continue;
}
FlowEdge& edge = _graph[v][_current[v]];
_work++;
if (edge.cap != Cap(0) && _height[v] == _height[edge.to] + 1) {
push(v, edge, s, t);
} else {
_current[v]++;
}
}
activate(v, s, t);
}
Cap max_flow(int s, int t) {
reset_flow();
std::fill(_excess.begin(), _excess.end(), Cap(0));
for (auto& edge : _graph[s]) {
Cap sent = edge.cap;
if (sent == Cap(0)) continue;
edge.cap = Cap(0);
_graph[edge.to][edge.rev].cap += sent;
_excess[edge.to] += sent;
}
global_relabel(s, t);
while (_highest >= 0) {
if (_buckets[_highest].empty()) {
_highest--;
continue;
}
int v = _buckets[_highest].back();
_buckets[_highest].pop_back();
if (!_active[v] || _height[v] != _highest) continue;
_active[v] = false;
discharge(v, s, t);
if (_work >= _work_limit) global_relabel(s, t);
}
return _excess[t];
}
std::vector<bool> source_side(int s) {
std::vector<bool> visited(_n, false);
int head = 0;
int tail = 0;
visited[s] = true;
_queue[tail++] = s;
while (head < tail) {
int v = _queue[head++];
for (const auto& edge : _graph[v]) {
if (edge.cap == Cap(0) || visited[edge.to]) continue;
visited[edge.to] = true;
_queue[tail++] = edge.to;
}
}
return visited;
}
void build_query_table() {
int log = 1;
while ((1LL << log) <= std::max(1, _n)) log++;
const Cap infinity = std::numeric_limits<Cap>::max();
_up.assign(log, std::vector<int>(_n, 0));
_minimum.assign(log, std::vector<Cap>(_n, infinity));
_depth.assign(_n, 0);
if (_n == 0) return;
std::vector<int> order;
order.reserve(_n);
order.push_back(0);
for (int i = 0; i < int(order.size()); i++) {
int v = order[i];
for (auto [to, cap] : _tree[v]) {
if (to == _up[0][v] && v != 0) continue;
_up[0][to] = v;
_minimum[0][to] = cap;
_depth[to] = _depth[v] + 1;
order.push_back(to);
}
}
for (int k = 1; k < log; k++) {
for (int v = 0; v < _n; v++) {
int middle = _up[k - 1][v];
_up[k][v] = _up[k - 1][middle];
_minimum[k][v] = std::min(_minimum[k - 1][v], _minimum[k - 1][middle]);
}
}
}
public:
GomoryHu() : GomoryHu(0) {}
explicit GomoryHu(int n) : _n(n) {
assert(0 <= n);
}
int size() const {
return _n;
}
int edge_count() const {
return int(_edges.size());
}
int add_edge(int u, int v, Cap cap) {
assert(0 <= u && u < _n);
assert(0 <= v && v < _n);
assert(Cap(0) <= cap);
_built = false;
int id = int(_edges.size());
_edges.push_back(Edge{u, v, cap});
return id;
}
void build() {
std::vector<Edge> flow_edges;
flow_edges.reserve(_edges.size());
for (auto edge : _edges) {
if (edge.u == edge.v || edge.cap == Cap(0)) continue;
if (edge.u > edge.v) std::swap(edge.u, edge.v);
flow_edges.push_back(edge);
}
std::sort(flow_edges.begin(), flow_edges.end(), [](const Edge& lhs, const Edge& rhs) {
return std::pair<int, int>(lhs.u, lhs.v) < std::pair<int, int>(rhs.u, rhs.v);
});
int unique_edges = 0;
for (const auto& edge : flow_edges) {
if (unique_edges > 0 && flow_edges[unique_edges - 1].u == edge.u &&
flow_edges[unique_edges - 1].v == edge.v) {
flow_edges[unique_edges - 1].cap += edge.cap;
} else {
flow_edges[unique_edges++] = edge;
}
}
flow_edges.resize(unique_edges);
_graph.assign(_n, {});
std::vector<int> degree(_n, 0);
for (const auto& edge : flow_edges) {
degree[edge.u]++;
degree[edge.v]++;
}
for (int v = 0; v < _n; v++) _graph[v].reserve(degree[v]);
for (const auto& edge : flow_edges) add_flow_edge(edge.u, edge.v, edge.cap);
_excess.resize(_n);
_height.resize(_n);
_height_count.resize(2 * _n + 1);
_current.resize(_n);
_active.resize(_n);
_buckets.resize(2 * _n + 1);
_queue.resize(_n);
long long arc_count = 0;
for (const auto& edges : _graph) arc_count += int(edges.size());
_work_limit = std::max(1LL, 4 * arc_count + _n);
_parent.assign(_n, 0);
_cut_value.assign(_n, std::numeric_limits<Cap>::max());
for (int s = 1; s < _n; s++) {
int t = _parent[s];
Cap flow = max_flow(s, t);
std::vector<bool> cut = source_side(s);
for (int v = s + 1; v < _n; v++) {
if (_parent[v] == t && cut[v]) _parent[v] = s;
}
if (cut[_parent[t]]) {
_parent[s] = _parent[t];
_parent[t] = s;
_cut_value[s] = _cut_value[t];
_cut_value[t] = flow;
} else {
_cut_value[s] = flow;
}
}
_tree.assign(_n, {});
_tree_edges.clear();
if (_n > 0) _tree_edges.reserve(_n - 1);
for (int v = 1; v < _n; v++) {
int p = _parent[v];
Cap cap = _cut_value[v];
_tree_edges.push_back(Edge{v, p, cap});
_tree[v].emplace_back(p, cap);
_tree[p].emplace_back(v, cap);
}
build_query_table();
_built = true;
}
const std::vector<Edge>& tree_edges() const {
assert(_built);
return _tree_edges;
}
const std::vector<int>& parent() const {
assert(_built);
return _parent;
}
const std::vector<Cap>& cut_values() const {
assert(_built);
return _cut_value;
}
Cap min_cut(int u, int v) const {
assert(_built);
assert(0 <= u && u < _n);
assert(0 <= v && v < _n);
assert(u != v);
Cap result = std::numeric_limits<Cap>::max();
if (_depth[u] < _depth[v]) std::swap(u, v);
int difference = _depth[u] - _depth[v];
for (int k = 0; difference > 0; k++, difference >>= 1) {
if ((difference & 1) == 0) continue;
result = std::min(result, _minimum[k][u]);
u = _up[k][u];
}
if (u == v) return result;
for (int k = int(_up.size()) - 1; k >= 0; k--) {
if (_up[k][u] == _up[k][v]) continue;
result = std::min(result, _minimum[k][u]);
result = std::min(result, _minimum[k][v]);
u = _up[k][u];
v = _up[k][v];
}
result = std::min(result, _minimum[0][u]);
result = std::min(result, _minimum[0][v]);
return result;
}
};
} // namespace flow
} // namespace m1une
#line 1 "graph/flow/min_cost_flow.hpp"
#line 6 "graph/flow/min_cost_flow.hpp"
#include <bit>
#line 15 "graph/flow/min_cost_flow.hpp"
#line 18 "graph/flow/min_cost_flow.hpp"
namespace m1une {
namespace flow {
template <class Cap, class Cost>
struct MinCostFlow {
struct Edge {
int from;
int to;
Cap cap;
Cap flow;
Cost cost;
};
private:
struct InternalEdge {
int to;
int rev;
Cap cap;
Cost cost;
};
int _n;
std::vector<std::pair<int, int>> _pos;
std::vector<std::vector<InternalEdge>> _g;
bool _has_negative_cost;
bool _has_flow;
template <class Key>
struct RadixHeap {
using Unsigned = std::make_unsigned_t<Key>;
static constexpr int bits = std::numeric_limits<Unsigned>::digits;
std::array<std::vector<std::pair<Unsigned, int>>, bits + 1> bucket;
Unsigned last = 0;
std::size_t count = 0;
static int index(Unsigned first, Unsigned second) {
return int(std::bit_width(first ^ second));
}
void clear() {
for (auto& values : bucket) values.clear();
last = 0;
count = 0;
}
bool empty() const {
return count == 0;
}
void push(Key key, int vertex) {
Unsigned value = static_cast<Unsigned>(key);
assert(last <= value);
bucket[index(value, last)].emplace_back(value, vertex);
count++;
}
std::pair<Key, int> pop() {
if (bucket[0].empty()) {
int i = 1;
while (bucket[i].empty()) i++;
last = bucket[i][0].first;
for (const auto& value : bucket[i]) {
last = std::min(last, value.first);
}
for (const auto& value : bucket[i]) {
bucket[index(value.first, last)].push_back(value);
}
bucket[i].clear();
}
auto [key, vertex] = bucket[0].back();
bucket[0].pop_back();
count--;
return {static_cast<Key>(key), vertex};
}
};
template <class Key>
struct BinaryHeap {
using Value = std::pair<Key, int>;
std::vector<Value> heap;
void clear() {
heap.clear();
}
bool empty() const {
return heap.empty();
}
void push(Key key, int vertex) {
heap.emplace_back(key, vertex);
std::push_heap(heap.begin(), heap.end(), std::greater<Value>());
}
Value pop() {
std::pop_heap(heap.begin(), heap.end(), std::greater<Value>());
Value result = heap.back();
heap.pop_back();
return result;
}
};
template <
class Key,
bool UseRadix =
std::numeric_limits<Key>::is_integer && sizeof(Key) <= 8
>
struct HeapSelector {
using Type = BinaryHeap<Key>;
};
template <class Key>
struct HeapSelector<Key, true> {
using Type = RadixHeap<Key>;
};
bool use_network_simplex(int s, int t, Cap flow_limit) const {
if (_has_negative_cost) return false;
if (_pos.size() < 64) return false;
auto add_saturated = [](Cap first, Cap second) {
const Cap maximum = std::numeric_limits<Cap>::max();
return maximum - first < second ? maximum : first + second;
};
struct TerminalCapacity {
Cap total = Cap(0);
std::array<Cap, 7> largest{};
};
auto add_capacity = [&](TerminalCapacity& terminal, Cap cap) {
terminal.total = add_saturated(terminal.total, cap);
for (Cap& current : terminal.largest) {
if (cap <= current) break;
std::swap(cap, current);
}
};
TerminalCapacity source;
for (const auto& e : _g[s]) {
if (e.to == s) continue;
add_capacity(source, e.cap);
}
TerminalCapacity sink;
for (const auto& e : _g[t]) {
if (e.to == t) continue;
Cap cap = _g[e.to][e.rev].cap;
add_capacity(sink, cap);
}
Cap target = std::min(
flow_limit,
std::min(source.total, sink.total)
);
if (target == Cap(0)) return false;
auto requires_eight_arcs = [&](const TerminalCapacity& terminal) {
Cap sum = Cap(0);
for (Cap cap : terminal.largest) {
sum = add_saturated(sum, cap);
}
return sum < target;
};
return requires_eight_arcs(source) && requires_eight_arcs(sink);
}
std::pair<Cap, Cost> network_simplex_flow(
int s,
int t,
Cap flow_limit
) {
struct ResidualArc {
int edge;
bool reverse;
};
using Solver = BoundedMinCostFlow<Cap, Cost, Cost>;
std::vector<ResidualArc> arcs;
arcs.reserve(2 * _pos.size());
for (int i = 0; i < int(_pos.size()); i++) {
auto [from, idx] = _pos[i];
const auto& e = _g[from][idx];
const auto& reverse = _g[e.to][e.rev];
if (e.cap != Cap(0)) {
arcs.push_back(ResidualArc{i, false});
}
if (reverse.cap != Cap(0)) {
arcs.push_back(ResidualArc{i, true});
}
}
auto add_saturated = [](Cap first, Cap second, bool& exact) {
const Cap maximum = std::numeric_limits<Cap>::max();
if (maximum - first < second) {
exact = false;
return maximum;
}
return first + second;
};
bool source_capacity_exact = true;
Cap source_capacity = Cap(0);
for (const auto& e : _g[s]) {
if (e.to == s) continue;
source_capacity = add_saturated(
source_capacity,
e.cap,
source_capacity_exact
);
}
bool sink_capacity_exact = true;
Cap sink_capacity = Cap(0);
for (const auto& e : _g[t]) {
if (e.to == t) continue;
sink_capacity = add_saturated(
sink_capacity,
_g[e.to][e.rev].cap,
sink_capacity_exact
);
}
Cap target = std::min(
flow_limit,
std::min(source_capacity, sink_capacity)
);
if (target == Cap(0)) return {Cap(0), Cost(0)};
struct ArcData {
int from;
int to;
Cap cap;
Cost cost;
};
auto arc_data = [&](const ResidualArc& arc) {
auto [from, idx] = _pos[arc.edge];
const auto& e = _g[from][idx];
const auto& reverse = _g[e.to][e.rev];
return arc.reverse
? ArcData{e.to, from, reverse.cap, reverse.cost}
: ArcData{from, e.to, e.cap, e.cost};
};
auto apply_flow = [&](const ResidualArc& arc, Cap amount) {
auto [from, idx] = _pos[arc.edge];
auto& e = _g[from][idx];
auto& reverse = _g[e.to][e.rev];
if (arc.reverse) {
reverse.cap -= amount;
e.cap += amount;
} else {
e.cap -= amount;
reverse.cap += amount;
}
};
bool target_infeasible = false;
if (
source_capacity_exact && sink_capacity_exact &&
target == source_capacity && target == sink_capacity
) {
Solver terminal_solver(_n);
terminal_solver.reserve_edges(int(arcs.size()));
std::vector<Cap> balance(_n, Cap(0));
std::vector<int> internal_arcs;
std::vector<int> fixed_arcs;
internal_arcs.reserve(arcs.size());
fixed_arcs.reserve(_g[s].size() + _g[t].size());
Cost fixed_cost = Cost(0);
for (int i = 0; i < int(arcs.size()); i++) {
ArcData data = arc_data(arcs[i]);
if (data.from == s) {
if (data.to == s) continue;
fixed_arcs.push_back(i);
fixed_cost += Cost(data.cap) * data.cost;
if (data.to != t) balance[data.to] += data.cap;
} else if (data.to == t) {
if (data.from == t) continue;
fixed_arcs.push_back(i);
fixed_cost += Cost(data.cap) * data.cost;
balance[data.from] -= data.cap;
} else if (data.to != s && data.from != t) {
terminal_solver.add_edge(
data.from,
data.to,
Cap(0),
data.cap,
data.cost
);
internal_arcs.push_back(i);
}
}
auto terminal_result = terminal_solver.min_cost_flow(balance);
if (terminal_result) {
for (int i : fixed_arcs) {
apply_flow(arcs[i], arc_data(arcs[i]).cap);
}
for (int i = 0; i < int(internal_arcs.size()); i++) {
apply_flow(
arcs[internal_arcs[i]],
terminal_result->flow(i)
);
}
_has_flow = true;
return {target, fixed_cost + terminal_result->cost};
}
target_infeasible = true;
}
Solver solver(_n);
solver.reserve_edges(int(arcs.size()));
for (const auto& arc : arcs) {
ArcData data = arc_data(arc);
solver.add_edge(
data.from,
data.to,
Cap(0),
data.cap,
data.cost
);
}
Cap sent = target;
std::optional<typename Solver::Result> result;
if (
!target_infeasible &&
target != std::numeric_limits<Cap>::max()
) {
result = solver.min_cost_st_flow(s, t, target);
}
if (!result) {
MaxFlow<Cap> feasible(_n);
feasible.reserve_edges(int(arcs.size()));
for (const auto& arc : arcs) {
auto [from, idx] = _pos[arc.edge];
const auto& e = _g[from][idx];
const auto& reverse = _g[e.to][e.rev];
if (arc.reverse) {
feasible.add_edge(e.to, from, reverse.cap);
} else {
feasible.add_edge(from, e.to, e.cap);
}
}
sent = feasible.max_flow(s, t, target);
if (sent == Cap(0)) return {Cap(0), Cost(0)};
result = solver.min_cost_st_flow(s, t, sent);
}
assert(result.has_value());
for (int i = 0; i < int(arcs.size()); i++) {
auto [from, idx] = _pos[arcs[i].edge];
auto& e = _g[from][idx];
auto& reverse = _g[e.to][e.rev];
Cap amount = result->flow(i);
if (arcs[i].reverse) {
reverse.cap -= amount;
e.cap += amount;
} else {
e.cap -= amount;
reverse.cap += amount;
}
}
_has_flow = true;
return {sent, result->cost};
}
void init_potential(int s, std::vector<Cost>& potential, Cost cost_inf) const {
if (!_has_negative_cost && !_has_flow) {
potential.assign(_n, Cost(0));
return;
}
potential.assign(_n, cost_inf);
potential[s] = Cost(0);
for (int iter = 0; iter < _n - 1; iter++) {
bool updated = false;
for (int v = 0; v < _n; v++) {
if (potential[v] == cost_inf) continue;
for (const auto& e : _g[v]) {
if (e.cap == Cap(0)) continue;
Cost nd = potential[v] + e.cost;
if (nd < potential[e.to]) {
potential[e.to] = nd;
updated = true;
}
}
}
if (!updated) break;
}
for (int v = 0; v < _n; v++) {
if (potential[v] == cost_inf) potential[v] = Cost(0);
}
}
public:
MinCostFlow() : MinCostFlow(0) {}
explicit MinCostFlow(int n)
: _n(n), _g(n), _has_negative_cost(false), _has_flow(false) {
assert(0 <= n);
}
int size() const {
return _n;
}
int edge_count() const {
return int(_pos.size());
}
void reserve_edges(int edge_count) {
assert(0 <= edge_count);
_pos.reserve(edge_count);
if (_n == 0 || edge_count == 0 ||
2 * std::size_t(edge_count) < std::size_t(_n)) {
return;
}
const std::size_t average_degree =
(3 * std::size_t(edge_count) + std::size_t(_n) - 1)
/ std::size_t(_n);
for (auto& edges : _g) edges.reserve(average_degree);
}
void reserve_edges(int edge_count, const std::vector<int>& degrees) {
assert(0 <= edge_count);
assert(int(degrees.size()) == _n);
_pos.reserve(edge_count);
for (int v = 0; v < _n; v++) {
assert(0 <= degrees[v]);
_g[v].reserve(degrees[v]);
}
}
int add_edge(int from, int to, Cap cap, Cost cost) {
assert(0 <= from && from < _n);
assert(0 <= to && to < _n);
assert(Cap(0) <= cap);
_has_negative_cost = _has_negative_cost || cost < Cost(0);
int id = int(_pos.size());
int from_id = int(_g[from].size());
int to_id = int(_g[to].size());
if (from == to) to_id++;
_pos.emplace_back(from, from_id);
_g[from].push_back(InternalEdge{to, to_id, cap, cost});
_g[to].push_back(InternalEdge{from, from_id, Cap(0), -cost});
return id;
}
Edge get_edge(int i) const {
assert(0 <= i && i < int(_pos.size()));
auto [from, idx] = _pos[i];
const auto& e = _g[from][idx];
const auto& re = _g[e.to][e.rev];
return Edge{from, e.to, e.cap + re.cap, re.cap, e.cost};
}
std::vector<Edge> edges() const {
std::vector<Edge> result;
result.reserve(_pos.size());
for (int i = 0; i < int(_pos.size()); i++) result.push_back(get_edge(i));
return result;
}
std::pair<Cap, Cost> flow(int s, int t) {
return flow(s, t, std::numeric_limits<Cap>::max());
}
std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
assert(Cap(0) <= flow_limit);
if (flow_limit == Cap(0)) return {Cap(0), Cost(0)};
if constexpr (
std::numeric_limits<Cap>::is_integer &&
std::numeric_limits<Cap>::is_signed &&
std::numeric_limits<Cost>::is_signed
) {
if (use_network_simplex(s, t, flow_limit)) {
return network_simplex_flow(s, t, flow_limit);
}
}
auto result = slope(s, t, flow_limit);
return result.back();
}
std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
return slope(s, t, std::numeric_limits<Cap>::max());
}
std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
assert(Cap(0) <= flow_limit);
const Cost cost_inf = std::numeric_limits<Cost>::max() / Cost(4);
std::vector<Cost> potential, dist(_n);
std::vector<int> prev_v(_n), prev_e(_n);
std::vector<int> settled;
settled.reserve(_n);
typename HeapSelector<Cost>::Type que;
init_potential(s, potential, cost_inf);
std::vector<std::pair<Cap, Cost>> result;
result.emplace_back(Cap(0), Cost(0));
Cap flow = 0;
Cost cost = 0;
while (flow < flow_limit) {
std::fill(dist.begin(), dist.end(), cost_inf);
dist[s] = Cost(0);
settled.clear();
que.clear();
que.push(Cost(0), s);
while (!que.empty()) {
auto [d, v] = que.pop();
if (dist[v] != d) continue;
settled.push_back(v);
if (v == t) break;
for (int i = 0; i < int(_g[v].size()); i++) {
const auto& e = _g[v][i];
if (e.cap == Cap(0)) continue;
Cost nd = d + e.cost + potential[v] - potential[e.to];
if (nd >= dist[e.to]) continue;
dist[e.to] = nd;
prev_v[e.to] = v;
prev_e[e.to] = i;
que.push(nd, e.to);
}
}
if (dist[t] == cost_inf) break;
for (int v : settled) {
potential[v] += dist[v] - dist[t];
}
Cap add = flow_limit - flow;
for (int v = t; v != s; v = prev_v[v]) {
add = std::min(add, _g[prev_v[v]][prev_e[v]].cap);
}
Cost path_cost = potential[t] - potential[s];
for (int v = t; v != s; v = prev_v[v]) {
auto& e = _g[prev_v[v]][prev_e[v]];
e.cap -= add;
_g[e.to][e.rev].cap += add;
}
flow += add;
cost += Cost(add) * path_cost;
result.emplace_back(flow, cost);
}
_has_flow = _has_flow || flow != Cap(0);
return result;
}
};
} // namespace flow
} // namespace m1une
#line 9 "graph/flow/flow.hpp"
#line 1 "graph/grid.hpp"
#line 8 "graph/grid.hpp"
#line 10 "graph/grid.hpp"
namespace m1une {
namespace graph {
struct Grid {
private:
int _h;
int _w;
public:
static constexpr std::array<int, 4> di4 = {-1, 0, 1, 0};
static constexpr std::array<int, 4> dj4 = {0, 1, 0, -1};
static constexpr std::array<int, 8> di8 = {-1, -1, -1, 0, 0, 1, 1, 1};
static constexpr std::array<int, 8> dj8 = {-1, 0, 1, -1, 1, -1, 0, 1};
Grid() : _h(0), _w(0) {}
Grid(int h, int w) : _h(h), _w(w) {
assert(0 <= h);
assert(0 <= w);
}
int height() const {
return _h;
}
int width() const {
return _w;
}
int size() const {
return _h * _w;
}
bool empty() const {
return size() == 0;
}
bool inside(int i, int j) const {
return 0 <= i && i < _h && 0 <= j && j < _w;
}
int id(int i, int j) const {
assert(inside(i, j));
return i * _w + j;
}
std::pair<int, int> pos(int v) const {
assert(0 <= v && v < size());
return {v / _w, v % _w};
}
std::vector<std::pair<int, int>> adj4(int i, int j) const {
assert(inside(i, j));
std::vector<std::pair<int, int>> result;
result.reserve(4);
for (int k = 0; k < 4; k++) {
int ni = i + di4[k], nj = j + dj4[k];
if (inside(ni, nj)) result.emplace_back(ni, nj);
}
return result;
}
std::vector<std::pair<int, int>> adj8(int i, int j) const {
assert(inside(i, j));
std::vector<std::pair<int, int>> result;
result.reserve(8);
for (int k = 0; k < 8; k++) {
int ni = i + di8[k], nj = j + dj8[k];
if (inside(ni, nj)) result.emplace_back(ni, nj);
}
return result;
}
std::vector<int> adj4_ids(int v) const {
auto [i, j] = pos(v);
std::vector<int> result;
result.reserve(4);
for (auto [ni, nj] : adj4(i, j)) result.push_back(id(ni, nj));
return result;
}
std::vector<int> adj8_ids(int v) const {
auto [i, j] = pos(v);
std::vector<int> result;
result.reserve(8);
for (auto [ni, nj] : adj8(i, j)) result.push_back(id(ni, nj));
return result;
}
Graph<int> graph4() const {
return graph4([](int, int) { return true; });
}
Graph<int> graph8() const {
return graph8([](int, int) { return true; });
}
template <class Passable>
Graph<int> graph4(Passable passable) const {
Graph<int> g(size());
for (int i = 0; i < _h; i++) {
for (int j = 0; j < _w; j++) {
if (!passable(i, j)) continue;
int v = id(i, j);
for (auto [ni, nj] : adj4(i, j)) {
if (!passable(ni, nj)) continue;
int to = id(ni, nj);
if (v < to) g.add_edge(v, to);
}
}
}
return g;
}
template <class Passable>
Graph<int> graph8(Passable passable) const {
Graph<int> g(size());
for (int i = 0; i < _h; i++) {
for (int j = 0; j < _w; j++) {
if (!passable(i, j)) continue;
int v = id(i, j);
for (auto [ni, nj] : adj8(i, j)) {
if (!passable(ni, nj)) continue;
int to = id(ni, nj);
if (v < to) g.add_edge(v, to);
}
}
}
return g;
}
};
} // namespace graph
} // namespace m1une
#line 1 "graph/range_edge_graph.hpp"
#line 6 "graph/range_edge_graph.hpp"
#line 8 "graph/range_edge_graph.hpp"
namespace m1une {
namespace graph {
struct RangeEdgeNode {
int vertex;
int left;
int right;
};
template <class T>
class RangeEdgeGraph {
struct SegmentNode {
int left = 0;
int right = 0;
int from_vertex = -1;
int to_vertex = -1;
};
int _n;
Graph<T> _graph;
std::vector<SegmentNode> _segment;
void assert_point(int point) const {
(void)point;
assert(0 <= point && point < _n);
}
void assert_range(int left, int right) const {
(void)left;
(void)right;
assert(0 <= left && left <= right && right <= _n);
}
void build(int node, int left, int right) {
_segment[node].left = left;
_segment[node].right = right;
if (right - left == 1) {
_segment[node].from_vertex = left;
_segment[node].to_vertex = left;
return;
}
int middle = (left + right) / 2;
build(node * 2, left, middle);
build(node * 2 + 1, middle, right);
int from_vertex = _graph.add_vertex();
int to_vertex = _graph.add_vertex();
_segment[node].from_vertex = from_vertex;
_segment[node].to_vertex = to_vertex;
_graph.add_directed_edge(_segment[node * 2].from_vertex, from_vertex, T());
_graph.add_directed_edge(_segment[node * 2 + 1].from_vertex, from_vertex, T());
_graph.add_directed_edge(to_vertex, _segment[node * 2].to_vertex, T());
_graph.add_directed_edge(to_vertex, _segment[node * 2 + 1].to_vertex, T());
}
void collect(int node, int left, int right, bool from_side,
std::vector<RangeEdgeNode>& result) const {
const auto& current = _segment[node];
if (right <= current.left || current.right <= left) return;
if (left <= current.left && current.right <= right) {
int vertex = from_side ? current.from_vertex : current.to_vertex;
result.push_back(RangeEdgeNode{vertex, current.left, current.right});
return;
}
collect(node * 2, left, right, from_side, result);
collect(node * 2 + 1, left, right, from_side, result);
}
public:
RangeEdgeGraph() : RangeEdgeGraph(0) {}
explicit RangeEdgeGraph(int point_count)
: _n(point_count),
_graph(point_count),
_segment(point_count == 0 ? 1 : point_count * 4) {
assert(point_count >= 0);
if (point_count != 0) build(1, 0, point_count);
}
int size() const {
return _n;
}
int point_vertex(int point) const {
assert_point(point);
return point;
}
int add_vertex() {
return _graph.add_vertex();
}
Graph<T>& graph() {
return _graph;
}
const Graph<T>& graph() const {
return _graph;
}
std::vector<RangeEdgeNode> from_range_nodes(int left, int right) const {
assert_range(left, right);
std::vector<RangeEdgeNode> result;
if (left != right) collect(1, left, right, true, result);
return result;
}
std::vector<RangeEdgeNode> to_range_nodes(int left, int right) const {
assert_range(left, right);
std::vector<RangeEdgeNode> result;
if (left != right) collect(1, left, right, false, result);
return result;
}
int add_point_to_point(int from, int to, T cost) {
assert_point(from);
assert_point(to);
return _graph.add_directed_edge(from, to, cost);
}
void add_point_to_range(int from, int left, int right, T cost) {
assert_point(from);
for (const auto& node : to_range_nodes(left, right)) {
_graph.add_directed_edge(from, node.vertex, cost);
}
}
void add_range_to_point(int left, int right, int to, T cost) {
assert_point(to);
for (const auto& node : from_range_nodes(left, right)) {
_graph.add_directed_edge(node.vertex, to, cost);
}
}
int add_range_to_range(int from_left, int from_right, int to_left, int to_right,
T cost) {
assert_range(from_left, from_right);
assert_range(to_left, to_right);
if (from_left == from_right || to_left == to_right) return -1;
int auxiliary = add_vertex();
for (const auto& node : from_range_nodes(from_left, from_right)) {
_graph.add_directed_edge(node.vertex, auxiliary, cost);
}
for (const auto& node : to_range_nodes(to_left, to_right)) {
_graph.add_directed_edge(auxiliary, node.vertex, T());
}
return auxiliary;
}
};
} // namespace graph
} // namespace m1une
#line 1 "graph/replacement_paths.hpp"
#line 11 "graph/replacement_paths.hpp"
#line 14 "graph/replacement_paths.hpp"
namespace m1une {
namespace graph {
struct GraphPath {
std::vector<int> vertices;
std::vector<int> edges;
};
template <class T>
struct EdgeReplacementPathsResult {
GraphPath path;
std::vector<T> replacement_dist;
T inf;
bool reachable(int path_edge_index) const {
assert(0 <= path_edge_index && path_edge_index < int(replacement_dist.size()));
return replacement_dist[path_edge_index] != inf;
}
};
template <class T>
struct VertexReplacementPathsResult {
GraphPath path;
std::vector<T> replacement_dist;
T inf;
bool reachable(int path_vertex_index) const {
assert(0 <= path_vertex_index && path_vertex_index < int(replacement_dist.size()));
return replacement_dist[path_vertex_index] != inf;
}
};
namespace internal {
template <class T>
T replacement_paths_safe_add(T a, T b, T inf) {
if (a >= inf || b >= inf) return inf;
if (a > inf - b) return inf;
return a + b;
}
template <class T>
DijkstraResult<T> replacement_paths_dijkstra(const Graph<T>& g, int s, T inf) {
int n = g.size();
assert(0 <= s && s < n);
DijkstraResult<T> result;
result.dist.assign(n, inf);
result.reached.assign(n, false);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.inf = inf;
using P = std::pair<T, int>;
std::priority_queue<P, std::vector<P>, std::greater<P>> que;
result.dist[s] = T(0);
result.reached[s] = true;
que.emplace(T(0), s);
while (!que.empty()) {
auto [d, v] = que.top();
que.pop();
if (result.dist[v] != d) continue;
for (const auto& e : g[v]) {
if (!e.alive) continue;
T nd = replacement_paths_safe_add(d, e.cost, inf);
if (result.dist[e.to] <= nd) continue;
result.reached[e.to] = true;
result.dist[e.to] = nd;
result.parent[e.to] = v;
result.parent_edge[e.to] = e.id;
que.emplace(nd, e.to);
}
}
return result;
}
template <class T>
std::vector<Edge<T>> replacement_paths_validate_graph(const Graph<T>& g, T inf) {
assert(T(0) < inf);
std::vector<int> occurrence(g.edge_count(), 0);
std::vector<Edge<T>> edge_by_id(g.edge_count());
for (int v = 0; v < g.size(); v++) {
for (const auto& e : g[v]) {
assert(e.from == v);
assert(0 <= e.to && e.to < g.size());
assert(0 <= e.id && e.id < g.edge_count());
if (e.alive) assert(T(0) < e.cost);
if (occurrence[e.id] == 0) {
edge_by_id[e.id] = e;
} else {
assert(occurrence[e.id] == 1);
const auto& other = edge_by_id[e.id];
assert(e.from == other.to && e.to == other.from);
assert(e.cost == other.cost && e.alive == other.alive);
}
occurrence[e.id]++;
}
}
for (int id = 0; id < g.edge_count(); id++) {
// add_edge creates exactly two mutually reversed arcs with one logical id.
assert(occurrence[id] == 2);
}
return edge_by_id;
}
template <class T>
void replacement_paths_validate_path(const Graph<T>& g, const GraphPath& path,
const std::vector<Edge<T>>& edge_by_id,
const DijkstraResult<T>& from_s, T inf) {
assert(!path.vertices.empty());
assert(path.edges.size() + 1 == path.vertices.size());
std::vector<char> used_vertex(g.size(), false);
for (int v : path.vertices) {
assert(0 <= v && v < g.size());
assert(!used_vertex[v]);
used_vertex[v] = true;
}
T path_cost = T(0);
for (int i = 0; i < int(path.edges.size()); i++) {
int id = path.edges[i];
assert(0 <= id && id < g.edge_count());
assert(g.is_edge_alive(id));
const auto& e = edge_by_id[id];
int u = path.vertices[i];
int v = path.vertices[i + 1];
assert((e.from == u && e.to == v) || (e.from == v && e.to == u));
assert(T(0) < e.cost);
path_cost = replacement_paths_safe_add(path_cost, e.cost, inf);
}
assert(from_s.reachable(path.vertices.back()));
assert(path_cost == from_s.dist[path.vertices.back()]);
}
template <class T>
GraphPath replacement_paths_restore_path(const DijkstraResult<T>& result, int s, int t) {
assert(result.reachable(t));
GraphPath path;
for (int v = t; v != s; v = result.parent[v]) {
assert(v != -1 && result.parent[v] != -1 && result.parent_edge[v] != -1);
path.vertices.push_back(v);
path.edges.push_back(result.parent_edge[v]);
}
path.vertices.push_back(s);
std::reverse(path.vertices.begin(), path.vertices.end());
std::reverse(path.edges.begin(), path.edges.end());
return path;
}
template <class T>
struct ReplacementPathsData {
GraphPath path;
std::vector<T> dist_s;
std::vector<T> dist_t;
std::vector<int> block;
std::vector<char> is_path_edge;
std::vector<Edge<T>> edge_by_id;
T inf;
};
template <class T>
ReplacementPathsData<T> replacement_paths_prepare(const Graph<T>& g, const GraphPath& path,
T inf, const DijkstraResult<T>* known_from_s) {
auto edge_by_id = replacement_paths_validate_graph(g, inf);
int s = path.vertices.front();
int t = path.vertices.back();
auto computed_from_s = known_from_s == nullptr
? replacement_paths_dijkstra(g, s, inf)
: DijkstraResult<T>();
const auto& from_s = known_from_s == nullptr ? computed_from_s : *known_from_s;
replacement_paths_validate_path(g, path, edge_by_id, from_s, inf);
auto from_t = replacement_paths_dijkstra(g, t, inf);
int n = g.size();
std::vector<int> path_position(n, -1);
std::vector<char> is_path_edge(g.edge_count(), false);
for (int i = 0; i < int(path.vertices.size()); i++) path_position[path.vertices[i]] = i;
for (int id : path.edges) is_path_edge[id] = true;
std::vector<int> parent(n, -1);
for (int i = 0; i < int(path.edges.size()); i++) {
int v = path.vertices[i + 1];
parent[v] = path.vertices[i];
const auto& e = edge_by_id[path.edges[i]];
assert(replacement_paths_safe_add(from_s.dist[parent[v]], e.cost, inf) == from_s.dist[v]);
}
for (int v = 0; v < n; v++) {
if (!from_s.reachable(v) || v == s || path_position[v] != -1) continue;
for (const auto& e : g[v]) {
if (!e.alive || !from_s.reachable(e.to)) continue;
if (replacement_paths_safe_add(from_s.dist[e.to], e.cost, inf) != from_s.dist[v]) {
continue;
}
parent[v] = e.to;
break;
}
assert(parent[v] != -1);
assert(from_s.dist[parent[v]] < from_s.dist[v]);
}
std::vector<std::vector<int>> children(n);
for (int v = 0; v < n; v++) {
if (parent[v] != -1) children[parent[v]].push_back(v);
}
std::vector<int> block(n, -1);
block[s] = 0;
std::vector<int> stack = {s};
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
for (int to : children[v]) {
block[to] = path_position[to] == -1 ? block[v] : path_position[to];
stack.push_back(to);
}
}
for (int v = 0; v < n; v++) assert(!from_s.reachable(v) || block[v] != -1);
return {path, from_s.dist, from_t.dist, block, is_path_edge, edge_by_id, inf};
}
template <class T>
class ReplacementPathsRangeChmin {
private:
int _size;
std::vector<T> _lazy;
public:
ReplacementPathsRangeChmin(int n, T inf) : _size(1) {
while (_size < n) _size <<= 1;
_lazy.assign(2 * _size, inf);
}
void apply(int l, int r, T value) {
assert(0 <= l && l <= r && r <= _size);
for (l += _size, r += _size; l < r; l >>= 1, r >>= 1) {
if (l & 1) {
_lazy[l] = std::min(_lazy[l], value);
l++;
}
if (r & 1) {
--r;
_lazy[r] = std::min(_lazy[r], value);
}
}
}
std::vector<T> values(int n) {
for (int v = 1; v < _size; v++) {
_lazy[2 * v] = std::min(_lazy[2 * v], _lazy[v]);
_lazy[2 * v + 1] = std::min(_lazy[2 * v + 1], _lazy[v]);
}
return std::vector<T>(_lazy.begin() + _size, _lazy.begin() + _size + n);
}
};
template <class T>
std::vector<T> replacement_paths_solve_edges(const ReplacementPathsData<T>& data) {
int answer_size = int(data.path.edges.size());
ReplacementPathsRangeChmin<T> range_chmin(answer_size, data.inf);
for (const auto& e : data.edge_by_id) {
if (!e.alive || data.is_path_edge[e.id]) continue;
int u = e.from;
int v = e.to;
if (data.block[u] == -1 || data.block[v] == -1 || data.block[u] == data.block[v]) continue;
if (data.block[u] > data.block[v]) std::swap(u, v);
int a = data.block[u];
int b = data.block[v];
T candidate = replacement_paths_safe_add(data.dist_s[u], e.cost, data.inf);
candidate = replacement_paths_safe_add(candidate, data.dist_t[v], data.inf);
if (candidate == data.inf) continue;
range_chmin.apply(a, b, candidate);
}
return range_chmin.values(answer_size);
}
template <class T>
T replacement_paths_without_vertex(const Graph<T>& g, int s, int t, int removed, T inf) {
if (s == removed || t == removed) return inf;
std::vector<T> dist(g.size(), inf);
using P = std::pair<T, int>;
std::priority_queue<P, std::vector<P>, std::greater<P>> que;
dist[s] = T(0);
que.emplace(T(0), s);
while (!que.empty()) {
auto [d, v] = que.top();
que.pop();
if (dist[v] != d) continue;
for (const auto& e : g[v]) {
if (!e.alive || e.to == removed) continue;
T nd = replacement_paths_safe_add(d, e.cost, inf);
if (dist[e.to] <= nd) continue;
dist[e.to] = nd;
que.emplace(nd, e.to);
}
}
return dist[t];
}
template <class T>
std::vector<T> replacement_paths_solve_vertices(const Graph<T>& g,
const ReplacementPathsData<T>& data) {
// One edge can cross an edge cut, but a vertex-avoiding path may enter and
// leave the failed vertex's tree block through two different detour edges.
int path_size = int(data.path.vertices.size());
std::vector<T> answer(path_size, data.inf);
int s = data.path.vertices.front();
int t = data.path.vertices.back();
for (int i = 1; i + 1 < path_size; i++) {
answer[i] = replacement_paths_without_vertex(g, s, t, data.path.vertices[i], data.inf);
}
return answer;
}
} // namespace internal
template <class T>
EdgeReplacementPathsResult<T> edge_replacement_paths(
const Graph<T>& g, const GraphPath& path, T inf = std::numeric_limits<T>::max() / T(4)) {
assert(!path.vertices.empty());
auto data = internal::replacement_paths_prepare(
g, path, inf, static_cast<const DijkstraResult<T>*>(nullptr));
auto replacement_dist = internal::replacement_paths_solve_edges(data);
return {path, replacement_dist, inf};
}
template <class T>
EdgeReplacementPathsResult<T> edge_replacement_paths(
const Graph<T>& g, int s, int t, T inf = std::numeric_limits<T>::max() / T(4)) {
assert(0 <= s && s < g.size());
assert(0 <= t && t < g.size());
auto from_s = internal::replacement_paths_dijkstra(g, s, inf);
assert(from_s.reachable(t));
auto path = internal::replacement_paths_restore_path(from_s, s, t);
auto data = internal::replacement_paths_prepare(g, path, inf, &from_s);
auto replacement_dist = internal::replacement_paths_solve_edges(data);
return {path, replacement_dist, inf};
}
template <class T>
VertexReplacementPathsResult<T> vertex_replacement_paths(
const Graph<T>& g, const GraphPath& path, T inf = std::numeric_limits<T>::max() / T(4)) {
assert(!path.vertices.empty());
auto data = internal::replacement_paths_prepare(
g, path, inf, static_cast<const DijkstraResult<T>*>(nullptr));
auto replacement_dist = internal::replacement_paths_solve_vertices(g, data);
return {path, replacement_dist, inf};
}
template <class T>
VertexReplacementPathsResult<T> vertex_replacement_paths(
const Graph<T>& g, int s, int t, T inf = std::numeric_limits<T>::max() / T(4)) {
assert(0 <= s && s < g.size());
assert(0 <= t && t < g.size());
auto from_s = internal::replacement_paths_dijkstra(g, s, inf);
assert(from_s.reachable(t));
auto path = internal::replacement_paths_restore_path(from_s, s, t);
auto data = internal::replacement_paths_prepare(g, path, inf, &from_s);
auto replacement_dist = internal::replacement_paths_solve_vertices(g, data);
return {path, replacement_dist, inf};
}
} // namespace graph
} // namespace m1une
#line 1 "graph/tree/all.hpp"
#line 1 "graph/tree/cartesian_tree.hpp"
#line 10 "graph/tree/cartesian_tree.hpp"
#line 12 "graph/tree/cartesian_tree.hpp"
namespace m1une {
namespace tree {
struct CartesianTree {
int root;
std::vector<int> parent;
std::vector<int> left;
std::vector<int> right;
private:
int _n;
void check_vertex(int v) const {
assert(0 <= v && v < _n);
}
public:
CartesianTree() : root(-1), _n(0) {}
template <class T, class Compare = std::less<T>>
explicit CartesianTree(const std::vector<T>& a, Compare comp = Compare()) : root(-1), _n(0) {
build(a, comp);
}
template <class T, class Compare = std::less<T>>
void build(const std::vector<T>& a, Compare comp = Compare()) {
assert(a.size() <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
_n = int(a.size());
root = -1;
parent.assign(_n, -1);
left.assign(_n, -1);
right.assign(_n, -1);
std::vector<int> stack;
stack.reserve(_n);
for (int i = 0; i < _n; i++) {
int last = -1;
while (!stack.empty() && comp(a[i], a[stack.back()])) {
last = stack.back();
stack.pop_back();
}
if (last != -1) {
left[i] = last;
parent[last] = i;
}
if (!stack.empty()) {
right[stack.back()] = i;
parent[i] = stack.back();
}
stack.push_back(i);
}
if (!stack.empty()) root = stack.front();
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
int parent_or_self(int v) const {
check_vertex(v);
return parent[v] == -1 ? v : parent[v];
}
std::vector<int> parent_with_root_self() const {
std::vector<int> result = parent;
if (root != -1) result[root] = root;
return result;
}
std::vector<std::pair<int, int>> edges() const {
std::vector<std::pair<int, int>> result;
if (_n == 0) return result;
result.reserve(_n - 1);
for (int v = 0; v < _n; v++) {
if (parent[v] != -1) result.emplace_back(parent[v], v);
}
return result;
}
m1une::graph::Graph<int> to_graph() const {
m1une::graph::Graph<int> g(_n);
for (int v = 0; v < _n; v++) {
if (parent[v] != -1) g.add_edge(parent[v], v);
}
return g;
}
};
template <class T, class Compare = std::less<T>>
CartesianTree cartesian_tree(const std::vector<T>& a, Compare comp = Compare()) {
CartesianTree result;
result.build(a, comp);
return result;
}
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/centroid_decomposition.hpp"
#line 6 "graph/tree/centroid_decomposition.hpp"
#line 8 "graph/tree/centroid_decomposition.hpp"
namespace m1une {
namespace tree {
template <class T = int>
struct CentroidDecomposition {
int n;
std::vector<int> parent;
std::vector<int> depth;
std::vector<int> order;
std::vector<int> roots;
std::vector<std::vector<int>> children;
private:
std::vector<int> _subtree_size;
std::vector<int> _work_parent;
std::vector<char> _removed;
void build_component(const m1une::graph::Graph<T>& g, int start, int p, int d) {
std::vector<int> nodes;
std::vector<int> stack = {start};
_work_parent[start] = -2;
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
nodes.push_back(v);
for (const auto& e : g[v]) {
if (!e.alive || _removed[e.to]) continue;
if (_work_parent[e.to] != -1) continue;
_work_parent[e.to] = v;
stack.push_back(e.to);
}
}
for (int v : nodes) _subtree_size[v] = 1;
for (int i = int(nodes.size()) - 1; i >= 0; i--) {
int v = nodes[i];
if (_work_parent[v] >= 0) _subtree_size[_work_parent[v]] += _subtree_size[v];
}
int total = int(nodes.size());
int centroid = start;
int best = total + 1;
for (int v : nodes) {
int largest = total - _subtree_size[v];
for (const auto& e : g[v]) {
if (!e.alive || _removed[e.to]) continue;
if (_work_parent[e.to] == v) largest = std::max(largest, _subtree_size[e.to]);
}
if (largest < best) {
best = largest;
centroid = v;
}
}
for (int v : nodes) _work_parent[v] = -1;
parent[centroid] = p;
depth[centroid] = d;
order.push_back(centroid);
if (p == -1) {
roots.push_back(centroid);
} else {
children[p].push_back(centroid);
}
_removed[centroid] = true;
for (const auto& e : g[centroid]) {
if (!e.alive || _removed[e.to]) continue;
build_component(g, e.to, centroid, d + 1);
}
}
public:
CentroidDecomposition() : n(0) {}
explicit CentroidDecomposition(const m1une::graph::Graph<T>& g) {
build(g);
}
void build(const m1une::graph::Graph<T>& g) {
n = g.size();
parent.assign(n, -1);
depth.assign(n, -1);
order.clear();
order.reserve(n);
roots.clear();
children.assign(n, {});
_subtree_size.assign(n, 0);
_work_parent.assign(n, -1);
_removed.assign(n, false);
for (int v = 0; v < n; v++) {
if (depth[v] == -1) build_component(g, v, -1, 0);
}
}
int size() const {
return n;
}
bool empty() const {
return n == 0;
}
int root() const {
return roots.empty() ? -1 : roots[0];
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/cumulative_sum.hpp"
#line 8 "graph/tree/cumulative_sum.hpp"
#line 1 "monoid/add.hpp"
namespace m1une {
namespace monoid {
// Monoid for addition (Range Sum).
template <typename T>
struct Add {
using value_type = T;
static constexpr bool commutative = true;
// Returns the identity element for addition, which is 0.
static constexpr T id() {
return T(0);
}
// Returns the sum of a and b.
static constexpr T op(const T& a, const T& b) {
return a + b;
}
static constexpr T inv(const T& x) {
return -x;
}
};
} // namespace monoid
} // namespace m1une
#line 1 "monoid/concept.hpp"
#line 5 "monoid/concept.hpp"
namespace m1une {
namespace monoid {
// Concept to check if a type satisfies the requirements of a Monoid.
// A Monoid must have a `value_type`, an identity element `id()`, and an associative binary operation `op()`.
template <typename M>
concept IsMonoid = requires(typename M::value_type a, typename M::value_type b) {
// 1. Must define `value_type`
typename M::value_type;
// 2. Must have a static method `id()` returning `value_type`
{ M::id() } -> std::same_as<typename M::value_type>;
// 3. Must have a static method `op(a, b)` returning `value_type`
{ M::op(a, b) } -> std::same_as<typename M::value_type>;
};
// Concept for groups. A type satisfying this concept must also obey the group
// laws; concepts can check the interface but not the algebraic properties.
template <typename M>
concept IsGroup = IsMonoid<M> && requires(typename M::value_type a) {
{ M::inv(a) } -> std::same_as<typename M::value_type>;
};
// Concept for commutative groups. Commutativity is a semantic requirement and
// cannot be checked by a C++ concept.
template <typename M>
concept IsCommutativeGroup = IsGroup<M>;
} // namespace monoid
} // namespace m1une
#line 12 "graph/tree/cumulative_sum.hpp"
namespace m1une {
namespace tree {
// Static cumulative products on root paths. Values are attached to vertices by
// default; set EdgeValues to true to index them by graph edge id instead.
template <m1une::monoid::IsCommutativeGroup Group, bool EdgeValues = false>
class TreeCumulativeProduct {
public:
using value_type = typename Group::value_type;
private:
int _n = 0;
int _root = -1;
std::vector<int> _parent;
std::vector<int> _depth;
std::vector<int> _head;
std::vector<value_type> _prefix;
void check_vertex(int vertex) const {
assert(0 <= vertex && vertex < _n);
}
public:
TreeCumulativeProduct() = default;
template <class EdgeCost>
explicit TreeCumulativeProduct(
const m1une::graph::Graph<EdgeCost>& graph,
const std::vector<value_type>& values,
int root = 0
) {
build(graph, values, root);
}
template <class EdgeCost>
void build(
const m1une::graph::Graph<EdgeCost>& graph,
const std::vector<value_type>& values,
int root = 0
) {
_n = graph.size();
_root = _n == 0 ? -1 : root;
assert(
int(values.size())
== (EdgeValues ? graph.edge_count() : graph.size())
);
_parent.assign(_n, -2);
_depth.assign(_n, 0);
_head.assign(_n, -1);
_prefix.assign(_n, Group::id());
if (_n == 0) return;
assert(0 <= root && root < _n);
std::vector<int> parent_edge(_n, -1);
std::vector<int> order;
order.reserve(_n);
std::vector<int> stack = {root};
_parent[root] = -1;
while (!stack.empty()) {
int vertex = stack.back();
stack.pop_back();
order.push_back(vertex);
for (const auto& edge : graph[vertex]) {
if (!edge.alive || _parent[edge.to] != -2) continue;
_parent[edge.to] = vertex;
parent_edge[edge.to] = edge.id;
_depth[edge.to] = _depth[vertex] + 1;
stack.push_back(edge.to);
}
}
assert(int(order.size()) == _n);
std::vector<int> subtree_size(_n, 1);
std::vector<int> heavy(_n, -1);
for (int index = _n - 1; index > 0; index--) {
int vertex = order[index];
int parent = _parent[vertex];
subtree_size[parent] += subtree_size[vertex];
if (
heavy[parent] == -1
|| subtree_size[heavy[parent]] < subtree_size[vertex]
) {
heavy[parent] = vertex;
}
}
std::vector<std::pair<int, int>> starts;
starts.emplace_back(root, root);
while (!starts.empty()) {
auto [start, head] = starts.back();
starts.pop_back();
for (
int vertex = start;
vertex != -1;
vertex = heavy[vertex]
) {
_head[vertex] = head;
for (const auto& edge : graph[vertex]) {
if (
edge.alive && _parent[edge.to] == vertex
&& edge.to != heavy[vertex]
) {
starts.emplace_back(edge.to, edge.to);
}
}
}
}
if constexpr (!EdgeValues) _prefix[root] = values[root];
for (int vertex : order) {
if (vertex == root) continue;
if constexpr (EdgeValues) {
assert(0 <= parent_edge[vertex]);
_prefix[vertex] = Group::op(
_prefix[_parent[vertex]],
values[parent_edge[vertex]]
);
} else {
_prefix[vertex] = Group::op(
_prefix[_parent[vertex]],
values[vertex]
);
}
}
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
int root() const {
return _root;
}
int lca(int first, int second) const {
check_vertex(first);
check_vertex(second);
while (_head[first] != _head[second]) {
if (_depth[_head[first]] < _depth[_head[second]]) {
std::swap(first, second);
}
first = _parent[_head[first]];
}
return _depth[first] < _depth[second] ? first : second;
}
// Product on the root-to-vertex path. The root vertex is included for
// vertex values; no edge lies above it in edge-value mode.
value_type prod(int vertex) const {
check_vertex(vertex);
return _prefix[vertex];
}
// Product on the simple path from first to second. Both endpoints are
// included for vertex values.
value_type prod(int first, int second) const {
int ancestor = lca(first, second);
value_type result = Group::op(_prefix[first], _prefix[second]);
result = Group::op(result, Group::inv(_prefix[ancestor]));
if constexpr (EdgeValues) {
result = Group::op(result, Group::inv(_prefix[ancestor]));
} else if (_parent[ancestor] != -1) {
result = Group::op(
result,
Group::inv(_prefix[_parent[ancestor]])
);
}
return result;
}
};
template <m1une::monoid::IsCommutativeGroup Group>
using TreeEdgeCumulativeProduct = TreeCumulativeProduct<Group, true>;
template <class T, bool EdgeValues = false>
class TreeCumulativeSum
: public TreeCumulativeProduct<m1une::monoid::Add<T>, EdgeValues> {
private:
using Base =
TreeCumulativeProduct<m1une::monoid::Add<T>, EdgeValues>;
public:
using Base::Base;
T sum(int vertex) const {
return Base::prod(vertex);
}
T sum(int first, int second) const {
return Base::prod(first, second);
}
};
template <class T>
using TreeEdgeCumulativeSum = TreeCumulativeSum<T, true>;
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/diameter.hpp"
#line 6 "graph/tree/diameter.hpp"
#line 8 "graph/tree/diameter.hpp"
namespace m1une {
namespace tree {
template <class T = int>
struct TreeDiameter {
T cost;
int edge_count;
int from;
int to;
std::vector<int> vertices;
std::vector<int> edge_ids;
bool empty() const {
return vertices.empty();
}
};
namespace internal {
template <class T>
struct FarthestResult {
int vertex;
std::vector<char> seen;
std::vector<T> dist;
std::vector<int> parent;
std::vector<int> parent_edge;
};
template <class T>
FarthestResult<T> farthest_from(const m1une::graph::Graph<T>& g, int start) {
int n = g.size();
FarthestResult<T> result;
result.vertex = start;
result.seen.assign(n, false);
result.dist.assign(n, T(0));
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
std::vector<int> stack = {start};
result.seen[start] = true;
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
if (result.dist[result.vertex] < result.dist[v]) result.vertex = v;
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (result.seen[e.to]) continue;
result.seen[e.to] = true;
result.dist[e.to] = result.dist[v] + e.cost;
result.parent[e.to] = v;
result.parent_edge[e.to] = e.id;
stack.push_back(e.to);
}
}
return result;
}
} // namespace internal
template <class T>
TreeDiameter<T> tree_diameter(const m1une::graph::Graph<T>& g) {
int n = g.size();
TreeDiameter<T> best;
best.cost = T(0);
best.edge_count = 0;
best.from = -1;
best.to = -1;
if (n == 0) return best;
std::vector<char> done(n, false);
for (int start = 0; start < n; start++) {
if (done[start]) continue;
auto first = internal::farthest_from(g, start);
for (int v = 0; v < n; v++) {
if (first.seen[v]) done[v] = true;
}
auto second = internal::farthest_from(g, first.vertex);
int a = first.vertex;
int b = second.vertex;
T cost = second.dist[b];
if (best.from != -1 && !(best.cost < cost)) continue;
best.cost = cost;
best.from = a;
best.to = b;
best.vertices.clear();
best.edge_ids.clear();
for (int v = b; v != -1; v = second.parent[v]) {
best.vertices.push_back(v);
if (v != a) best.edge_ids.push_back(second.parent_edge[v]);
}
std::reverse(best.vertices.begin(), best.vertices.end());
std::reverse(best.edge_ids.begin(), best.edge_ids.end());
best.edge_count = int(best.edge_ids.size());
}
return best;
}
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/distance_frequency.hpp"
#line 10 "graph/tree/distance_frequency.hpp"
#line 14 "graph/tree/distance_frequency.hpp"
namespace m1une {
namespace tree {
namespace distance_frequency_detail {
template <class Mint, class T>
std::vector<Mint> count_ordered_pairs(
const m1une::graph::Graph<T>& tree,
const CentroidDecomposition<T>& decomposition
) {
const int size = tree.size();
std::vector<Mint> count(static_cast<std::size_t>(size));
std::vector<char> removed(std::size_t(size), false);
std::vector<Mint> histogram;
std::vector<std::pair<int, int>> stack;
std::vector<int> parent(std::size_t(size), -1);
for (int centroid : decomposition.order) {
std::vector<Mint> total(1, Mint(1));
for (const auto& edge : tree[centroid]) {
if (!edge.alive || removed[std::size_t(edge.to)]) continue;
histogram.clear();
stack.clear();
stack.emplace_back(edge.to, 1);
parent[std::size_t(edge.to)] = centroid;
while (!stack.empty()) {
const auto [vertex, distance] = stack.back();
stack.pop_back();
if (int(histogram.size()) <= distance) {
histogram.resize(std::size_t(distance + 1));
}
histogram[std::size_t(distance)] += Mint(1);
for (const auto& next : tree[vertex]) {
if (!next.alive || removed[std::size_t(next.to)]) continue;
if (next.to == parent[std::size_t(vertex)]) continue;
parent[std::size_t(next.to)] = vertex;
stack.emplace_back(next.to, distance + 1);
}
}
if (total.size() < histogram.size()) {
total.resize(histogram.size());
}
for (std::size_t distance = 0; distance < histogram.size(); distance++) {
total[distance] += histogram[distance];
}
const std::vector<Mint> within_component =
m1une::fps::convolution(histogram, histogram);
const std::size_t limit = std::min(count.size(), within_component.size());
for (std::size_t distance = 0; distance < limit; distance++) {
count[distance] -= within_component[distance];
}
}
const std::vector<Mint> through_centroid =
m1une::fps::convolution(total, total);
const std::size_t limit = std::min(count.size(), through_centroid.size());
for (std::size_t distance = 0; distance < limit; distance++) {
count[distance] += through_centroid[distance];
}
removed[std::size_t(centroid)] = true;
}
return count;
}
inline std::uint64_t combine_residues(std::uint32_t first, std::uint32_t second) {
using First = m1une::math::ModInt<998244353>;
using Second = m1une::math::ModInt<924844033>;
static const std::uint64_t inverse = Second(First::mod()).inv().val();
const std::uint64_t offset =
(std::uint64_t(second) + Second::mod() - first % Second::mod()) %
Second::mod();
const std::uint64_t multiplier = offset * inverse % Second::mod();
return std::uint64_t(first) + std::uint64_t(First::mod()) * multiplier;
}
} // namespace distance_frequency_detail
template <class T>
std::vector<long long> tree_distance_frequency(
const m1une::graph::Graph<T>& tree
) {
const int size = tree.size();
assert(tree.edge_count() == std::max(0, size - 1));
if (size == 0) return {};
const CentroidDecomposition<T> decomposition(tree);
assert(decomposition.roots.size() == 1);
using First = m1une::math::ModInt<998244353>;
using Second = m1une::math::ModInt<924844033>;
assert(
std::uint64_t(size) * std::uint64_t(size - 1) <
std::uint64_t(First::mod()) * Second::mod()
);
const std::vector<First> first =
distance_frequency_detail::count_ordered_pairs<First>(
tree,
decomposition
);
const std::vector<Second> second =
distance_frequency_detail::count_ordered_pairs<Second>(
tree,
decomposition
);
std::vector<long long> result(static_cast<std::size_t>(size));
result[0] = size;
for (int distance = 1; distance < size; distance++) {
const std::uint64_t ordered =
distance_frequency_detail::combine_residues(
first[std::size_t(distance)].val(),
second[std::size_t(distance)].val()
);
assert((ordered & 1) == 0);
result[std::size_t(distance)] = static_cast<long long>(ordered / 2);
}
return result;
}
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/dsu_on_tree.hpp"
#line 7 "graph/tree/dsu_on_tree.hpp"
#line 9 "graph/tree/dsu_on_tree.hpp"
namespace m1une {
namespace tree {
template <class T = int>
struct DsuOnTree {
int n;
int root;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> depth;
std::vector<int> subtree_size;
std::vector<int> heavy_child;
std::vector<int> tin;
std::vector<int> tout;
std::vector<int> order;
std::vector<std::vector<int>> children;
DsuOnTree() : n(0), root(-1) {}
explicit DsuOnTree(
const m1une::graph::Graph<T>& graph,
int root_vertex = 0
) {
build(graph, root_vertex);
}
void build(
const m1une::graph::Graph<T>& graph,
int root_vertex = 0
) {
n = graph.size();
root = n == 0 ? -1 : root_vertex;
parent.assign(n, -2);
parent_edge.assign(n, -1);
depth.assign(n, 0);
subtree_size.assign(n, 1);
heavy_child.assign(n, -1);
tin.assign(n, -1);
tout.assign(n, -1);
order.clear();
order.reserve(n);
children.assign(n, {});
if (n == 0) return;
assert(0 <= root && root < n);
std::vector<int> stack;
stack.push_back(root);
parent[root] = -1;
while (!stack.empty()) {
int vertex = stack.back();
stack.pop_back();
tin[vertex] = int(order.size());
order.push_back(vertex);
for (const auto& edge : graph[vertex]) {
if (!edge.alive || parent[edge.to] != -2) continue;
parent[edge.to] = vertex;
parent_edge[edge.to] = edge.id;
depth[edge.to] = depth[vertex] + 1;
children[vertex].push_back(edge.to);
stack.push_back(edge.to);
}
}
assert(int(order.size()) == n);
for (int index = n - 1; index >= 0; --index) {
int vertex = order[index];
for (int child : children[vertex]) {
subtree_size[vertex] += subtree_size[child];
if (
heavy_child[vertex] == -1 ||
subtree_size[heavy_child[vertex]] < subtree_size[child]
) {
heavy_child[vertex] = child;
}
}
tout[vertex] = tin[vertex] + subtree_size[vertex];
}
}
int size() const {
return n;
}
bool empty() const {
return n == 0;
}
std::pair<int, int> subtree_range(int vertex) const {
assert(0 <= vertex && vertex < n);
return {tin[vertex], tout[vertex]};
}
// Runs DSU on tree. `add(v)` inserts one vertex into the maintained state,
// `remove(v)` erases it, and `answer(v)` observes the state for subtree(v).
template <class Add, class Remove, class Answer>
void run(Add add, Remove remove, Answer answer) const {
if (n == 0) return;
enum ActionType {
Process,
AddSubtree,
AddVertex,
AnswerVertex,
RemoveSubtree,
};
struct Action {
ActionType type;
int vertex;
bool keep;
};
std::vector<Action> actions;
actions.reserve(3 * std::size_t(n));
actions.push_back(Action{Process, root, true});
while (!actions.empty()) {
Action action = actions.back();
actions.pop_back();
int vertex = action.vertex;
if (action.type == AddSubtree) {
for (int index = tin[vertex]; index < tout[vertex]; ++index) {
add(order[index]);
}
} else if (action.type == AddVertex) {
add(vertex);
} else if (action.type == AnswerVertex) {
answer(vertex);
} else if (action.type == RemoveSubtree) {
for (int index = tin[vertex]; index < tout[vertex]; ++index) {
remove(order[index]);
}
} else {
if (!action.keep) {
actions.push_back(Action{
RemoveSubtree,
vertex,
false,
});
}
actions.push_back(Action{AnswerVertex, vertex, false});
actions.push_back(Action{AddVertex, vertex, false});
for (int child : children[vertex]) {
if (child != heavy_child[vertex]) {
actions.push_back(Action{
AddSubtree,
child,
false,
});
}
}
if (heavy_child[vertex] != -1) {
actions.push_back(Action{
Process,
heavy_child[vertex],
true,
});
}
for (int child : children[vertex]) {
if (child != heavy_child[vertex]) {
actions.push_back(Action{Process, child, false});
}
}
}
}
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/euler_tour.hpp"
#line 8 "graph/tree/euler_tour.hpp"
#line 10 "graph/tree/euler_tour.hpp"
namespace m1une {
namespace tree {
template <class T = int>
struct EulerTour {
using cost_type = T;
using edge_type = m1une::graph::Edge<T>;
int root;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> depth;
std::vector<T> dist;
std::vector<int> subtree_size;
std::vector<int> tin;
std::vector<int> tout;
std::vector<int> order;
std::vector<std::vector<int>> children;
private:
int _n;
void check_vertex(int v) const {
assert(0 <= v && v < _n);
assert(tin[v] != -1);
}
public:
EulerTour() : root(-1), _n(0) {}
explicit EulerTour(const m1une::graph::Graph<T>& g, int root_ = 0) {
build(g, root_);
}
void build(const m1une::graph::Graph<T>& g, int root_ = 0) {
_n = g.size();
root = _n == 0 ? -1 : root_;
parent.assign(_n, -2);
parent_edge.assign(_n, -1);
depth.assign(_n, 0);
dist.assign(_n, T(0));
subtree_size.assign(_n, 0);
tin.assign(_n, -1);
tout.assign(_n, -1);
order.clear();
order.reserve(_n);
children.assign(_n, {});
if (_n == 0) return;
assert(0 <= root && root < _n);
struct Frame {
int v;
int state;
};
std::vector<Frame> stack;
stack.push_back({root, 0});
parent[root] = -1;
while (!stack.empty()) {
Frame frame = stack.back();
stack.pop_back();
int v = frame.v;
if (frame.state == 0) {
tin[v] = int(order.size());
order.push_back(v);
stack.push_back({v, 1});
const auto& adj = g[v];
for (int i = int(adj.size()) - 1; i >= 0; --i) {
const auto& e = adj[i];
if (!e.alive) continue;
if (parent[e.to] != -2) continue;
parent[e.to] = v;
parent_edge[e.to] = e.id;
depth[e.to] = depth[v] + 1;
dist[e.to] = dist[v] + e.cost;
children[v].push_back(e.to);
stack.push_back({e.to, 0});
}
std::reverse(children[v].begin(), children[v].end());
} else {
subtree_size[v] = 1;
for (int child : children[v]) subtree_size[v] += subtree_size[child];
tout[v] = int(order.size());
}
}
}
int size() const {
return _n;
}
int visited_size() const {
return int(order.size());
}
bool empty() const {
return _n == 0;
}
bool is_ancestor(int u, int v) const {
check_vertex(u);
check_vertex(v);
return tin[u] <= tin[v] && tout[v] <= tout[u];
}
bool in_subtree(int v, int u) const {
return is_ancestor(u, v);
}
std::pair<int, int> subtree_range(int v, bool edge = false) const {
check_vertex(v);
return {tin[v] + (edge ? 1 : 0), tout[v]};
}
std::vector<int> subtree_vertices(int v) const {
check_vertex(v);
return std::vector<int>(order.begin() + tin[v], order.begin() + tout[v]);
}
template <class F>
void for_each_subtree(int v, F f) const {
auto [l, r] = subtree_range(v);
for (int i = l; i < r; ++i) f(order[i]);
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/heavy_light_decomposition.hpp"
#line 8 "graph/tree/heavy_light_decomposition.hpp"
#line 10 "graph/tree/heavy_light_decomposition.hpp"
namespace m1une {
namespace tree {
struct HldPathSegment {
int l;
int r;
bool reversed;
};
template <class T = int>
struct HeavyLightDecomposition {
using cost_type = T;
using edge_type = m1une::graph::Edge<T>;
int root;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> depth;
std::vector<T> dist;
std::vector<int> subtree_size;
std::vector<int> heavy;
std::vector<int> head;
std::vector<int> tin;
std::vector<int> tout;
std::vector<int> order;
private:
int _n;
void check_vertex(int v) const {
assert(0 <= v && v < _n);
assert(tin[v] != -1);
}
static void add_segment(std::vector<HldPathSegment>& result, int l, int r, bool reversed) {
if (l < r) result.push_back({l, r, reversed});
}
public:
HeavyLightDecomposition() : root(-1), _n(0) {}
explicit HeavyLightDecomposition(const m1une::graph::Graph<T>& g, int root_ = 0) {
build(g, root_);
}
void build(const m1une::graph::Graph<T>& g, int root_ = 0) {
_n = g.size();
root = _n == 0 ? -1 : root_;
parent.assign(_n, -2);
parent_edge.assign(_n, -1);
depth.assign(_n, 0);
dist.assign(_n, T(0));
subtree_size.assign(_n, 1);
heavy.assign(_n, -1);
head.assign(_n, -1);
tin.assign(_n, -1);
tout.assign(_n, -1);
order.clear();
order.reserve(_n);
if (_n == 0) return;
assert(0 <= root && root < _n);
std::vector<int> dfs_order;
dfs_order.reserve(_n);
std::vector<int> stack = {root};
parent[root] = -1;
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
dfs_order.push_back(v);
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (parent[e.to] != -2) continue;
parent[e.to] = v;
parent_edge[e.to] = e.id;
depth[e.to] = depth[v] + 1;
dist[e.to] = dist[v] + e.cost;
stack.push_back(e.to);
}
}
for (int i = int(dfs_order.size()) - 1; i >= 0; i--) {
int v = dfs_order[i];
if (parent[v] == -1) continue;
int p = parent[v];
subtree_size[p] += subtree_size[v];
if (heavy[p] == -1 || subtree_size[heavy[p]] < subtree_size[v]) heavy[p] = v;
}
order.assign(dfs_order.size(), -1);
int timer = 0;
std::vector<std::pair<int, int>> starts = {std::pair<int, int>{root, root}};
while (!starts.empty()) {
auto [start, h] = starts.back();
starts.pop_back();
for (int v = start; v != -1; v = heavy[v]) {
head[v] = h;
tin[v] = timer;
order[timer++] = v;
for (auto it = g[v].rbegin(); it != g[v].rend(); ++it) {
if (!it->alive) continue;
int to = it->to;
if (parent[to] != v || to == heavy[v]) continue;
starts.push_back({to, to});
}
}
}
for (int i = int(dfs_order.size()) - 1; i >= 0; i--) {
int v = dfs_order[i];
tout[v] = tin[v] + subtree_size[v];
}
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
bool is_ancestor(int u, int v) const {
check_vertex(u);
check_vertex(v);
return tin[u] <= tin[v] && tout[v] <= tout[u];
}
int lca(int u, int v) const {
check_vertex(u);
check_vertex(v);
while (head[u] != head[v]) {
if (depth[head[u]] < depth[head[v]]) std::swap(u, v);
u = parent[head[u]];
}
return depth[u] < depth[v] ? u : v;
}
int dist_edges(int u, int v) const {
int w = lca(u, v);
return depth[u] + depth[v] - 2 * depth[w];
}
T dist_cost(int u, int v) const {
int w = lca(u, v);
return dist[u] + dist[v] - dist[w] - dist[w];
}
int kth_ancestor(int v, int k) const {
check_vertex(v);
assert(0 <= k);
while (v != -1) {
int h = head[v];
int len = depth[v] - depth[h];
if (k <= len) return order[tin[v] - k];
k -= len + 1;
v = parent[h];
}
return -1;
}
int jump(int from, int to, int k) const {
check_vertex(from);
check_vertex(to);
assert(0 <= k);
int w = lca(from, to);
int up_len = depth[from] - depth[w];
int down_len = depth[to] - depth[w];
if (up_len + down_len < k) return -1;
if (k <= up_len) return kth_ancestor(from, k);
return kth_ancestor(to, down_len - (k - up_len));
}
std::pair<int, int> subtree_range(int v, bool edge = false) const {
check_vertex(v);
return {tin[v] + (edge ? 1 : 0), tout[v]};
}
std::vector<HldPathSegment> path_segments(int u, int v, bool edge = false) const {
check_vertex(u);
check_vertex(v);
std::vector<HldPathSegment> result, down;
while (head[u] != head[v]) {
if (depth[head[u]] >= depth[head[v]]) {
add_segment(result, tin[head[u]], tin[u] + 1, true);
u = parent[head[u]];
} else {
add_segment(down, tin[head[v]], tin[v] + 1, false);
v = parent[head[v]];
}
}
if (depth[u] >= depth[v]) {
add_segment(result, tin[v] + (edge ? 1 : 0), tin[u] + 1, true);
} else {
add_segment(down, tin[u] + (edge ? 1 : 0), tin[v] + 1, false);
}
std::reverse(down.begin(), down.end());
result.insert(result.end(), down.begin(), down.end());
return result;
}
template <class F>
void for_each_path(int u, int v, F f, bool edge = false) const {
for (auto seg : path_segments(u, v, edge)) f(seg.l, seg.r, seg.reversed);
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/mo_on_tree.hpp"
#line 7 "graph/tree/mo_on_tree.hpp"
#line 1 "algo/offline/mo.hpp"
#line 7 "algo/offline/mo.hpp"
#include <numeric>
#line 9 "algo/offline/mo.hpp"
namespace m1une {
namespace algo {
// Offline Mo's algorithm for half-open array ranges.
struct Mo {
struct Query {
int left;
int right;
int id;
};
private:
int _n;
std::vector<Query> _queries;
public:
Mo() : _n(0) {}
explicit Mo(int n) : _n(n) {
assert(0 <= n);
}
int size() const {
return _n;
}
int query_count() const {
return int(_queries.size());
}
bool empty() const {
return _queries.empty();
}
const std::vector<Query>& queries() const {
return _queries;
}
void reserve(int query_capacity) {
assert(0 <= query_capacity);
_queries.reserve(query_capacity);
}
void clear() {
_queries.clear();
}
// Adds [left, right) and returns its insertion-order ID.
int add_query(int left, int right) {
assert(0 <= left && left <= right && right <= _n);
int id = query_count();
_queries.push_back(Query{left, right, id});
return id;
}
// Returns query IDs in Mo order. A non-positive block size selects one
// automatically.
std::vector<int> order(int block_size = 0) const {
int query_size = query_count();
std::vector<int> result(query_size);
std::iota(result.begin(), result.end(), 0);
if (query_size == 0) return result;
if (block_size <= 0) {
block_size = std::max(1, int(_n / std::sqrt(static_cast<double>(query_size))));
}
std::sort(result.begin(), result.end(), [&](int first, int second) {
const Query& a = _queries[first];
const Query& b = _queries[second];
int first_block = a.left / block_size;
int second_block = b.left / block_size;
if (first_block != second_block) {
return first_block < second_block;
}
if (first_block & 1) return a.right > b.right;
return a.right < b.right;
});
return result;
}
// Maintains [left, right). Each movement callback receives the array index
// being inserted or erased. `answer(query_id)` stores or reports a result.
template <class AddLeft, class AddRight, class RemoveLeft, class RemoveRight, class Answer>
void run(AddLeft add_left, AddRight add_right, RemoveLeft remove_left, RemoveRight remove_right, Answer answer,
int block_size = 0) const {
int left = 0;
int right = 0;
for (int query_index : order(block_size)) {
const Query& query = _queries[query_index];
while (query.left < left) add_left(--left);
while (right < query.right) add_right(right++);
while (left < query.left) remove_left(left++);
while (query.right < right) remove_right(--right);
answer(query.id);
}
}
// Convenience overload for statistics whose update is independent of
// which side moves.
template <class Add, class Remove, class Answer>
void run(Add add, Remove remove, Answer answer, int block_size = 0) const {
run(add, add, remove, remove, answer, block_size);
}
};
} // namespace algo
} // namespace m1une
#line 11 "graph/tree/mo_on_tree.hpp"
namespace m1une {
namespace tree {
// Offline Mo's algorithm for static paths in a tree.
template <class T = int>
struct MoOnTree {
struct Query {
int from;
int to;
int left;
int right;
int extra;
int id;
bool edge;
};
int root;
std::vector<int> entry;
std::vector<int> exit;
std::vector<int> tour;
private:
int _n;
HeavyLightDecomposition<T> _hld;
m1une::algo::Mo _mo;
std::vector<Query> _queries;
void check_vertex(int vertex) const {
assert(0 <= vertex && vertex < _n);
assert(entry[vertex] != -1);
}
int add_path_query(int from, int to, bool edge) {
check_vertex(from);
check_vertex(to);
assert(_queries.empty() || _queries.front().edge == edge);
int original_from = from;
int original_to = to;
if (entry[from] > entry[to]) std::swap(from, to);
int ancestor = _hld.lca(from, to);
int left;
int right = entry[to] + 1;
int extra = -1;
if (ancestor == from) {
left = entry[from] + int(edge);
} else {
left = exit[from];
if (!edge) extra = ancestor;
}
int id = _mo.add_query(left, right);
_queries.push_back(Query{
original_from,
original_to,
left,
right,
extra,
id,
edge,
});
return id;
}
public:
MoOnTree() : root(-1), _n(0), _mo(0) {}
explicit MoOnTree(
const m1une::graph::Graph<T>& graph,
int root_vertex = 0
) : root(-1), _n(0), _mo(0) {
build(graph, root_vertex);
}
void build(
const m1une::graph::Graph<T>& graph,
int root_vertex = 0
) {
_n = graph.size();
root = _n == 0 ? -1 : root_vertex;
entry.assign(_n, -1);
exit.assign(_n, -1);
tour.clear();
tour.reserve(2 * _n);
_queries.clear();
_mo = m1une::algo::Mo(2 * _n);
_hld.build(graph, root_vertex);
if (_n == 0) return;
assert(0 <= root && root < _n);
for (int vertex = 0; vertex < _n; ++vertex) {
assert(_hld.parent[vertex] != -2);
}
std::vector<std::vector<int>> children(_n);
for (int vertex = 0; vertex < _n; ++vertex) {
int parent = _hld.parent[vertex];
if (parent != -1) children[parent].push_back(vertex);
}
struct Event {
int vertex;
bool leaving;
};
std::vector<Event> stack;
stack.reserve(2 * _n);
stack.push_back(Event{root, false});
while (!stack.empty()) {
Event event = stack.back();
stack.pop_back();
int vertex = event.vertex;
if (event.leaving) {
exit[vertex] = int(tour.size());
tour.push_back(vertex);
continue;
}
entry[vertex] = int(tour.size());
tour.push_back(vertex);
stack.push_back(Event{vertex, true});
const auto& child_list = children[vertex];
for (int index = int(child_list.size()) - 1; index >= 0; --index) {
stack.push_back(Event{child_list[index], false});
}
}
assert(int(tour.size()) == 2 * _n);
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
int query_count() const {
return int(_queries.size());
}
const std::vector<Query>& queries() const {
return _queries;
}
int parent(int vertex) const {
check_vertex(vertex);
return _hld.parent[vertex];
}
int parent_edge(int vertex) const {
check_vertex(vertex);
return _hld.parent_edge[vertex];
}
int depth(int vertex) const {
check_vertex(vertex);
return _hld.depth[vertex];
}
int lca(int first, int second) const {
check_vertex(first);
check_vertex(second);
return _hld.lca(first, second);
}
void reserve(int query_capacity) {
assert(0 <= query_capacity);
_queries.reserve(query_capacity);
_mo.reserve(query_capacity);
}
void clear() {
_queries.clear();
_mo.clear();
}
// Adds an inclusive vertex-path query and returns its insertion-order ID.
// Vertex and edge queries cannot be mixed in one collection.
int add_query(int from, int to) {
return add_path_query(from, to, false);
}
// Adds an edge-path query. Each edge is represented by its child vertex.
int add_edge_query(int from, int to) {
return add_path_query(from, to, true);
}
std::vector<int> order(int block_size = 0) const {
return _mo.order(block_size);
}
// `add(v)` and `remove(v)` maintain the current path. In edge mode, v
// always represents the real edge parent_edge(v).
template <class Add, class Remove, class Answer>
void run(
Add add,
Remove remove,
Answer answer,
int block_size = 0
) const {
bool edge_mode = !_queries.empty() && _queries.front().edge;
std::vector<char> active(_n, false);
auto toggle = [&](int tour_index) {
int vertex = tour[tour_index];
if (!edge_mode || vertex != root) {
if (active[vertex]) {
remove(vertex);
} else {
add(vertex);
}
}
active[vertex] = !active[vertex];
};
_mo.run(
toggle,
toggle,
[&](int query_id) {
int extra = _queries[query_id].extra;
if (extra != -1) {
assert(!active[extra]);
add(extra);
}
answer(query_id);
if (extra != -1) remove(extra);
},
block_size
);
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/range_contour_query.hpp"
#line 7 "graph/tree/range_contour_query.hpp"
#line 1 "graph/tree/rooted_tree.hpp"
#line 7 "graph/tree/rooted_tree.hpp"
#line 9 "graph/tree/rooted_tree.hpp"
namespace m1une {
namespace tree {
template <class T = int>
struct RootedTree {
using cost_type = T;
using edge_type = m1une::graph::Edge<T>;
int root;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> depth;
std::vector<T> dist;
std::vector<int> subtree_size;
std::vector<int> tin;
std::vector<int> tout;
std::vector<int> order;
std::vector<std::vector<int>> up;
private:
int _n;
int _log;
void check_vertex(int v) const {
assert(0 <= v && v < _n);
assert(tin[v] != -1);
}
public:
RootedTree() : root(-1), _n(0), _log(0) {}
explicit RootedTree(const m1une::graph::Graph<T>& g, int root_ = 0) {
build(g, root_);
}
void build(const m1une::graph::Graph<T>& g, int root_ = 0) {
_n = g.size();
root = _n == 0 ? -1 : root_;
_log = 1;
while ((1U << _log) <= (unsigned int)(std::max(1, _n))) _log++;
parent.assign(_n, -1);
parent_edge.assign(_n, -1);
depth.assign(_n, 0);
dist.assign(_n, T(0));
subtree_size.assign(_n, 0);
tin.assign(_n, -1);
tout.assign(_n, -1);
order.clear();
order.reserve(_n);
up.assign(_log, std::vector<int>(_n, -1));
if (_n == 0) return;
assert(0 <= root && root < _n);
struct Frame {
int v;
int state;
};
std::vector<char> visited(_n, false);
std::vector<Frame> stack;
stack.push_back({root, 0});
visited[root] = true;
int timer = 0;
while (!stack.empty()) {
Frame frame = stack.back();
stack.pop_back();
int v = frame.v;
if (frame.state == 0) {
tin[v] = timer++;
order.push_back(v);
up[0][v] = parent[v];
for (int k = 1; k < _log; k++) {
int p = up[k - 1][v];
up[k][v] = p == -1 ? -1 : up[k - 1][p];
}
stack.push_back({v, 1});
const auto& adj = g[v];
for (int i = int(adj.size()) - 1; i >= 0; i--) {
const auto& e = adj[i];
if (!e.alive) continue;
if (visited[e.to]) continue;
visited[e.to] = true;
parent[e.to] = v;
parent_edge[e.to] = e.id;
depth[e.to] = depth[v] + 1;
dist[e.to] = dist[v] + e.cost;
stack.push_back({e.to, 0});
}
} else {
subtree_size[v]++;
if (parent[v] != -1) subtree_size[parent[v]] += subtree_size[v];
tout[v] = timer;
}
}
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
int log() const {
return _log;
}
bool is_ancestor(int u, int v) const {
check_vertex(u);
check_vertex(v);
return tin[u] <= tin[v] && tout[v] <= tout[u];
}
bool in_subtree(int v, int u) const {
return is_ancestor(u, v);
}
int kth_ancestor(int v, int k) const {
check_vertex(v);
assert(0 <= k);
int bit = 0;
while (k > 0 && v != -1) {
if (k & 1) {
if (_log <= bit) return -1;
v = up[bit][v];
}
k >>= 1;
bit++;
}
return v;
}
int lca(int u, int v) const {
check_vertex(u);
check_vertex(v);
if (depth[u] < depth[v]) std::swap(u, v);
u = kth_ancestor(u, depth[u] - depth[v]);
if (u == v) return u;
for (int k = _log - 1; k >= 0; k--) {
if (up[k][u] != up[k][v]) {
u = up[k][u];
v = up[k][v];
}
}
return parent[u];
}
int dist_edges(int u, int v) const {
int w = lca(u, v);
return depth[u] + depth[v] - 2 * depth[w];
}
T dist_cost(int u, int v) const {
int w = lca(u, v);
return dist[u] + dist[v] - dist[w] - dist[w];
}
int jump(int from, int to, int k) const {
check_vertex(from);
check_vertex(to);
assert(0 <= k);
int w = lca(from, to);
int up_len = depth[from] - depth[w];
int down_len = depth[to] - depth[w];
if (up_len + down_len < k) return -1;
if (k <= up_len) return kth_ancestor(from, k);
return kth_ancestor(to, down_len - (k - up_len));
}
std::vector<int> path(int u, int v) const {
check_vertex(u);
check_vertex(v);
int w = lca(u, v);
std::vector<int> a, b;
for (int x = u; x != w; x = parent[x]) a.push_back(x);
a.push_back(w);
for (int x = v; x != w; x = parent[x]) b.push_back(x);
std::reverse(b.begin(), b.end());
a.insert(a.end(), b.begin(), b.end());
return a;
}
std::vector<int> path_edges(int u, int v) const {
check_vertex(u);
check_vertex(v);
int w = lca(u, v);
std::vector<int> a, b;
for (int x = u; x != w; x = parent[x]) a.push_back(parent_edge[x]);
for (int x = v; x != w; x = parent[x]) b.push_back(parent_edge[x]);
std::reverse(b.begin(), b.end());
a.insert(a.end(), b.begin(), b.end());
return a;
}
std::pair<int, int> subtree_range(int v) const {
check_vertex(v);
return {tin[v], tout[v]};
}
std::vector<int> subtree_vertices(int v) const {
check_vertex(v);
return std::vector<int>(order.begin() + tin[v], order.begin() + tout[v]);
}
};
} // namespace tree
} // namespace m1une
#line 13 "graph/tree/range_contour_query.hpp"
namespace m1une {
namespace tree {
namespace internal {
struct RangeContourPathEntry {
int centroid;
int distance;
int subtree;
};
struct RangeContourLayout {
int n = 0;
std::vector<std::vector<RangeContourPathEntry>> path;
std::vector<int> all_size;
std::vector<int> subtree_size;
template <class EdgeCost>
void build(const m1une::graph::Graph<EdgeCost>& graph) {
n = graph.size();
path.assign(n, {});
all_size.assign(n, 0);
subtree_size.assign(n, 0);
if (n == 0) return;
#ifndef NDEBUG
std::vector<int> incidence(graph.edge_count(), 0);
for (int vertex = 0; vertex < n; vertex++) {
for (const auto& edge : graph[vertex]) {
if (!edge.alive) continue;
assert(0 <= edge.id && edge.id < graph.edge_count());
incidence[edge.id]++;
}
}
int active_edges = 0;
for (int count : incidence) {
if (count == 0) continue;
assert(count == 2);
active_edges++;
}
assert(active_edges == n - 1);
#endif
RootedTree<EdgeCost> rooted(graph, 0);
assert(int(rooted.order.size()) == n);
CentroidDecomposition<EdgeCost> decomposition(graph);
for (int vertex = 0; vertex < n; vertex++) {
int previous = -1;
for (
int centroid = vertex;
centroid != -1;
centroid = decomposition.parent[centroid]
) {
int distance = rooted.dist_edges(vertex, centroid);
path[vertex].push_back(
RangeContourPathEntry{centroid, distance, previous}
);
all_size[centroid] = std::max(
all_size[centroid],
distance + 1
);
if (previous != -1) {
subtree_size[previous] = std::max(
subtree_size[previous],
distance + 1
);
}
previous = centroid;
}
}
}
};
template <m1une::monoid::IsCommutativeGroup Group>
class RangeContourFenwick {
public:
using T = typename Group::value_type;
private:
int _n = 0;
std::vector<T> _data;
T prefix_product(int right) const {
T result = Group::id();
while (right > 0) {
result = Group::op(result, _data[right]);
right -= right & -right;
}
return result;
}
public:
RangeContourFenwick() : _data(1, Group::id()) {}
explicit RangeContourFenwick(int n)
: _n(n), _data(n + 1, Group::id()) {
assert(0 <= n);
}
int size() const {
return _n;
}
void apply(int index, const T& value) {
assert(0 <= index && index < _n);
for (index++; index <= _n; index += index & -index) {
_data[index] = Group::op(_data[index], value);
}
}
T product(int left, int right) const {
left = std::max(left, 0);
right = std::min(right, _n);
if (right <= left) return Group::id();
return Group::op(
Group::inv(prefix_product(left)),
prefix_product(right)
);
}
void range_apply(int left, int right, const T& value) {
left = std::max(left, 0);
right = std::min(right, _n);
if (right <= left) return;
apply(left, value);
if (right < _n) apply(right, Group::inv(value));
}
T get(int index) const {
assert(0 <= index && index < _n);
return prefix_product(index + 1);
}
};
} // namespace internal
template <m1une::monoid::IsCommutativeGroup Group>
class VertexApplyRangeContourProduct {
public:
using T = typename Group::value_type;
private:
internal::RangeContourLayout _layout;
std::vector<T> _value;
std::vector<internal::RangeContourFenwick<Group>> _all;
std::vector<internal::RangeContourFenwick<Group>> _subtree;
void check_vertex(int vertex) const {
assert(0 <= vertex && vertex < size());
}
public:
VertexApplyRangeContourProduct() = default;
template <class EdgeCost>
explicit VertexApplyRangeContourProduct(
const m1une::graph::Graph<EdgeCost>& graph,
const std::vector<T>& initial = {}
) {
build(graph, initial);
}
template <class EdgeCost>
void build(
const m1une::graph::Graph<EdgeCost>& graph,
const std::vector<T>& initial = {}
) {
assert(initial.empty() || int(initial.size()) == graph.size());
_layout.build(graph);
const int n = _layout.n;
_value.assign(n, Group::id());
_all.assign(n, internal::RangeContourFenwick<Group>());
_subtree.assign(n, internal::RangeContourFenwick<Group>());
for (int index = 0; index < n; index++) {
_all[index] =
internal::RangeContourFenwick<Group>(_layout.all_size[index]);
_subtree[index] =
internal::RangeContourFenwick<Group>(
_layout.subtree_size[index]
);
}
if (!initial.empty()) {
for (int vertex = 0; vertex < n; vertex++) {
apply(vertex, initial[vertex]);
}
}
}
int size() const {
return _layout.n;
}
bool empty() const {
return size() == 0;
}
T get(int vertex) const {
check_vertex(vertex);
return _value[vertex];
}
void apply(int vertex, const T& value) {
check_vertex(vertex);
_value[vertex] = Group::op(_value[vertex], value);
for (const auto& entry : _layout.path[vertex]) {
_all[entry.centroid].apply(entry.distance, value);
if (entry.subtree != -1) {
_subtree[entry.subtree].apply(entry.distance, value);
}
}
}
void set(int vertex, const T& value) {
check_vertex(vertex);
apply(vertex, Group::op(Group::inv(_value[vertex]), value));
}
T prod(int vertex, int left_distance, int right_distance) const {
check_vertex(vertex);
assert(0 <= left_distance && left_distance <= right_distance);
T result = Group::id();
for (const auto& entry : _layout.path[vertex]) {
int left = left_distance - entry.distance;
int right = right_distance - entry.distance;
result = Group::op(
result,
_all[entry.centroid].product(left, right)
);
if (entry.subtree != -1) {
result = Group::op(
result,
Group::inv(
_subtree[entry.subtree].product(left, right)
)
);
}
}
return result;
}
};
template <m1une::monoid::IsCommutativeGroup Group>
class VertexGetRangeContourApply {
public:
using T = typename Group::value_type;
private:
internal::RangeContourLayout _layout;
std::vector<T> _base;
std::vector<internal::RangeContourFenwick<Group>> _all;
std::vector<internal::RangeContourFenwick<Group>> _subtree;
void check_vertex(int vertex) const {
assert(0 <= vertex && vertex < size());
}
public:
VertexGetRangeContourApply() = default;
template <class EdgeCost>
explicit VertexGetRangeContourApply(
const m1une::graph::Graph<EdgeCost>& graph,
const std::vector<T>& initial = {}
) {
build(graph, initial);
}
template <class EdgeCost>
void build(
const m1une::graph::Graph<EdgeCost>& graph,
const std::vector<T>& initial = {}
) {
assert(initial.empty() || int(initial.size()) == graph.size());
_layout.build(graph);
const int n = _layout.n;
_base = initial.empty() ? std::vector<T>(n, Group::id()) : initial;
_all.assign(n, internal::RangeContourFenwick<Group>());
_subtree.assign(n, internal::RangeContourFenwick<Group>());
for (int index = 0; index < n; index++) {
_all[index] =
internal::RangeContourFenwick<Group>(_layout.all_size[index]);
_subtree[index] =
internal::RangeContourFenwick<Group>(
_layout.subtree_size[index]
);
}
}
int size() const {
return _layout.n;
}
bool empty() const {
return size() == 0;
}
T get(int vertex) const {
check_vertex(vertex);
T result = _base[vertex];
for (const auto& entry : _layout.path[vertex]) {
result = Group::op(
result,
_all[entry.centroid].get(entry.distance)
);
if (entry.subtree != -1) {
result = Group::op(
result,
Group::inv(
_subtree[entry.subtree].get(entry.distance)
)
);
}
}
return result;
}
void point_apply(int vertex, const T& value) {
check_vertex(vertex);
_base[vertex] = Group::op(_base[vertex], value);
}
void set(int vertex, const T& value) {
check_vertex(vertex);
_base[vertex] = Group::op(
_base[vertex],
Group::op(Group::inv(get(vertex)), value)
);
}
void apply(
int vertex,
int left_distance,
int right_distance,
const T& value
) {
check_vertex(vertex);
assert(0 <= left_distance && left_distance <= right_distance);
for (const auto& entry : _layout.path[vertex]) {
int left = left_distance - entry.distance;
int right = right_distance - entry.distance;
_all[entry.centroid].range_apply(left, right, value);
if (entry.subtree != -1) {
_subtree[entry.subtree].range_apply(left, right, value);
}
}
}
};
template <class T>
class VertexAddRangeContourSum
: public VertexApplyRangeContourProduct<m1une::monoid::Add<T>> {
private:
using Base = VertexApplyRangeContourProduct<m1une::monoid::Add<T>>;
public:
using Base::Base;
void add(int vertex, const T& delta) {
Base::apply(vertex, delta);
}
T sum(int vertex, int left_distance, int right_distance) const {
return Base::prod(vertex, left_distance, right_distance);
}
};
template <class T>
class VertexGetRangeContourAdd
: public VertexGetRangeContourApply<m1une::monoid::Add<T>> {
private:
using Base = VertexGetRangeContourApply<m1une::monoid::Add<T>>;
public:
using Base::Base;
void add(int vertex, const T& delta) {
Base::point_apply(vertex, delta);
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/rerooting_dp.hpp"
#line 5 "graph/tree/rerooting_dp.hpp"
#line 7 "graph/tree/rerooting_dp.hpp"
namespace m1une {
namespace tree {
template <class T, class DP, class Merge, class AddVertex, class AddEdge>
std::vector<DP> rerooting_dp(const m1une::graph::Graph<T>& g, DP id, Merge merge, AddVertex add_vertex,
AddEdge add_edge) {
int n = g.size();
std::vector<int> parent(n, -2), parent_edge(n, -1), order;
order.reserve(n);
for (int root = 0; root < n; root++) {
if (parent[root] != -2) continue;
parent[root] = -1;
std::vector<int> stack = {root};
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
order.push_back(v);
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (parent[e.to] != -2) continue;
parent[e.to] = v;
parent_edge[e.to] = e.id;
stack.push_back(e.to);
}
}
}
std::vector<DP> down(n, id), outside(n, id), answer(n, id);
for (int i = n - 1; i >= 0; i--) {
int v = order[i];
DP acc = id;
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (parent[e.to] != v) continue;
acc = merge(acc, add_edge(down[e.to], e));
}
down[v] = add_vertex(acc, v);
}
for (int v : order) {
int d = int(g[v].size());
std::vector<DP> contrib(d, id);
for (int i = 0; i < d; i++) {
const auto& e = g[v][i];
if (!e.alive) continue;
if (parent[e.to] == v) {
contrib[i] = add_edge(down[e.to], e);
} else if (parent[v] == e.to && parent_edge[v] == e.id) {
contrib[i] = add_edge(outside[v], e);
}
}
std::vector<DP> pref(d + 1, id), suff(d + 1, id);
for (int i = 0; i < d; i++) pref[i + 1] = merge(pref[i], contrib[i]);
for (int i = d - 1; i >= 0; i--) suff[i] = merge(contrib[i], suff[i + 1]);
answer[v] = add_vertex(pref[d], v);
for (int i = 0; i < d; i++) {
const auto& e = g[v][i];
if (!e.alive) continue;
if (parent[e.to] != v) continue;
outside[e.to] = add_vertex(merge(pref[i], suff[i + 1]), v);
}
}
return answer;
}
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/rerooting_static_top_tree.hpp"
#line 10 "graph/tree/rerooting_static_top_tree.hpp"
#line 12 "graph/tree/rerooting_static_top_tree.hpp"
namespace m1une {
namespace tree {
namespace internal {
enum class RerootingStaticTopTreeNodeType {
Compress,
Rake,
AddEdge,
AddVertex,
};
enum class RerootingStaticTopTreeStepType {
CompressLower,
CompressUpper,
AddEdge,
RakeLeft,
RakeRight,
AddVertex,
};
} // namespace internal
template <class T, class Vertex, class Path, class Point, class CompressDown, class CompressUp, class Rake,
class AddEdgeDown, class AddEdgeUp, class AddVertex>
struct RerootingStaticTopTree {
using cost_type = T;
using vertex_type = Vertex;
using path_type = Path;
using point_type = Point;
using edge_type = m1une::graph::Edge<T>;
using node_type = internal::RerootingStaticTopTreeNodeType;
using step_type = internal::RerootingStaticTopTreeStepType;
struct Node {
node_type type;
int left = -1;
int right = -1;
int parent = -1;
int vertex = -1;
edge_type edge;
int size = 0;
int height = 1;
std::optional<Path> path_down;
std::optional<Path> path_up;
std::optional<Point> point;
};
struct RerootingStep {
step_type type;
int node = -1;
int sibling = -1;
int vertex = -1;
edge_type edge;
};
private:
int _n;
int _root;
int _root_node;
Point _point_id;
CompressDown _compress_down;
CompressUp _compress_up;
Rake _rake;
AddEdgeDown _add_edge_down;
AddEdgeUp _add_edge_up;
AddVertex _add_vertex;
std::vector<Vertex> _values;
std::vector<Node> _nodes;
std::vector<int> _vertex_node;
std::vector<int> _edge_node;
std::vector<int> _parent;
std::vector<int> _subtree_size;
std::vector<int> _heavy;
std::vector<edge_type> _heavy_edge;
std::vector<std::vector<edge_type>> _children;
static edge_type reversed_edge(edge_type e) {
std::swap(e.from, e.to);
return e;
}
const Path& node_path_down(int node) const {
assert(0 <= node && node < int(_nodes.size()));
assert(_nodes[node].path_down.has_value());
return *_nodes[node].path_down;
}
const Path& node_path_up(int node) const {
assert(0 <= node && node < int(_nodes.size()));
assert(_nodes[node].path_up.has_value());
return *_nodes[node].path_up;
}
const Point& node_point(int node) const {
assert(0 <= node && node < int(_nodes.size()));
assert(_nodes[node].point.has_value());
return *_nodes[node].point;
}
void set_parent(int child, int parent) {
if (child != -1) _nodes[child].parent = parent;
}
void recompute(int node) {
auto& x = _nodes[node];
if (x.type == node_type::Compress) {
x.path_down = _compress_down(node_path_down(x.left), node_path_down(x.right), x.edge);
x.path_up = _compress_up(node_path_up(x.right), node_path_up(x.left), reversed_edge(x.edge));
} else if (x.type == node_type::Rake) {
x.point = _rake(node_point(x.left), node_point(x.right));
} else if (x.type == node_type::AddEdge) {
x.point = _add_edge_down(node_path_down(x.left), x.edge);
} else {
const Point& side = x.left == -1 ? _point_id : node_point(x.left);
Path path = _add_vertex(side, _values[x.vertex], x.vertex);
x.path_down = path;
x.path_up = std::move(path);
}
}
int new_node(Node node) {
int id = int(_nodes.size());
_nodes.push_back(std::move(node));
set_parent(_nodes[id].left, id);
set_parent(_nodes[id].right, id);
recompute(id);
return id;
}
int new_compress(int left, int right, edge_type edge) {
Node node;
node.type = node_type::Compress;
node.left = left;
node.right = right;
node.edge = edge;
node.size = _nodes[left].size + _nodes[right].size;
node.height = std::max(_nodes[left].height, _nodes[right].height) + 1;
int id = new_node(std::move(node));
if (0 <= edge.id && edge.id < int(_edge_node.size())) _edge_node[edge.id] = id;
return id;
}
int new_rake(int left, int right) {
Node node;
node.type = node_type::Rake;
node.left = left;
node.right = right;
node.size = _nodes[left].size + _nodes[right].size;
node.height = std::max(_nodes[left].height, _nodes[right].height) + 1;
return new_node(std::move(node));
}
int new_add_edge(int child, edge_type edge) {
Node node;
node.type = node_type::AddEdge;
node.left = child;
node.edge = edge;
node.size = _nodes[child].size;
node.height = _nodes[child].height + 1;
int id = new_node(std::move(node));
if (0 <= edge.id && edge.id < int(_edge_node.size())) _edge_node[edge.id] = id;
return id;
}
int new_add_vertex(int side, int vertex) {
Node node;
node.type = node_type::AddVertex;
node.left = side;
node.vertex = vertex;
node.size = 1 + (side == -1 ? 0 : _nodes[side].size);
node.height = 1 + (side == -1 ? 0 : _nodes[side].height);
int id = new_node(std::move(node));
_vertex_node[vertex] = id;
return id;
}
int weighted_split(const std::vector<int>& nodes, int l, int r) const {
int total = 0;
for (int i = l; i < r; i++) total += _nodes[nodes[i]].size;
int left_sum = 0;
for (int i = l; i + 1 < r; i++) {
left_sum += _nodes[nodes[i]].size;
if (2 * left_sum >= total) return i + 1;
}
return r - 1;
}
int build_rake(const std::vector<int>& nodes, int l, int r) {
if (l == r) return -1;
if (l + 1 == r) return nodes[l];
int m = weighted_split(nodes, l, r);
return new_rake(build_rake(nodes, l, m), build_rake(nodes, m, r));
}
int build_compress(const std::vector<int>& nodes, const std::vector<edge_type>& edges, int l, int r) {
if (l + 1 == r) return nodes[l];
int m = weighted_split(nodes, l, r);
return new_compress(build_compress(nodes, edges, l, m), build_compress(nodes, edges, m, r), edges[m - 1]);
}
int build_vertex(int v) {
std::vector<int> side_nodes;
for (const auto& e : _children[v]) {
if (e.to == _heavy[v]) continue;
int child_path = build_path(e.to);
side_nodes.push_back(new_add_edge(child_path, e));
}
return new_add_vertex(build_rake(side_nodes, 0, int(side_nodes.size())), v);
}
int build_path(int start) {
std::vector<int> path_nodes;
std::vector<edge_type> path_edges;
for (int v = start; v != -1; v = _heavy[v]) {
path_nodes.push_back(build_vertex(v));
if (_heavy[v] != -1) path_edges.push_back(_heavy_edge[v]);
}
return build_compress(path_nodes, path_edges, 0, int(path_nodes.size()));
}
void recompute_up(int node) {
while (node != -1) {
recompute(node);
node = _nodes[node].parent;
}
}
public:
RerootingStaticTopTree(const m1une::graph::Graph<T>& g, const std::vector<Vertex>& values, Point point_id,
CompressDown compress_down, CompressUp compress_up, Rake rake,
AddEdgeDown add_edge_down, AddEdgeUp add_edge_up, AddVertex add_vertex, int root = 0)
: _n(g.size()),
_root(_n == 0 ? -1 : root),
_root_node(-1),
_point_id(std::move(point_id)),
_compress_down(std::move(compress_down)),
_compress_up(std::move(compress_up)),
_rake(std::move(rake)),
_add_edge_down(std::move(add_edge_down)),
_add_edge_up(std::move(add_edge_up)),
_add_vertex(std::move(add_vertex)),
_values(values) {
build(g, root);
}
void build(const m1une::graph::Graph<T>& g, int root = 0) {
_n = g.size();
_root = _n == 0 ? -1 : root;
assert(int(_values.size()) == _n);
_nodes.clear();
_vertex_node.assign(_n, -1);
_edge_node.assign(g.edge_count(), -1);
_parent.assign(_n, -2);
_subtree_size.assign(_n, 1);
_heavy.assign(_n, -1);
_heavy_edge.assign(_n, edge_type());
_children.assign(_n, {});
_root_node = -1;
if (_n == 0) return;
assert(0 <= root && root < _n);
assert(int(g.edges().size()) == _n - 1);
std::vector<int> order;
order.reserve(_n);
std::vector<int> stack = {root};
_parent[root] = -1;
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
order.push_back(v);
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (_parent[e.to] != -2) continue;
_parent[e.to] = v;
_children[v].push_back(e);
stack.push_back(e.to);
}
}
assert(int(order.size()) == _n);
for (int i = int(order.size()) - 1; i >= 0; i--) {
int v = order[i];
for (const auto& e : _children[v]) {
_subtree_size[v] += _subtree_size[e.to];
if (_heavy[v] == -1 || _subtree_size[_heavy[v]] < _subtree_size[e.to]) {
_heavy[v] = e.to;
_heavy_edge[v] = e;
}
}
}
_root_node = build_path(root);
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
int root() const {
return _root;
}
int root_node() const {
return _root_node;
}
int node_count() const {
return int(_nodes.size());
}
int height() const {
return _root_node == -1 ? 0 : _nodes[_root_node].height;
}
const std::vector<Node>& nodes() const {
return _nodes;
}
const Node& node(int id) const {
assert(0 <= id && id < int(_nodes.size()));
return _nodes[id];
}
int parent_node(int id) const {
return node(id).parent;
}
int vertex_node(int v) const {
assert(0 <= v && v < _n);
return _vertex_node[v];
}
int local_point_node(int v) const {
int id = vertex_node(v);
assert(_nodes[id].type == node_type::AddVertex);
return _nodes[id].left;
}
const Point& local_point(int v) const {
int point_node = local_point_node(v);
return point_node == -1 ? _point_id : node_point(point_node);
}
const Vertex& get(int v) const {
assert(0 <= v && v < _n);
return _values[v];
}
const Vertex& operator[](int v) const {
return get(v);
}
void set(int v, const Vertex& value) {
assert(0 <= v && v < _n);
assert(_vertex_node[v] != -1);
_values[v] = value;
recompute_up(_vertex_node[v]);
}
void set(int v, Vertex&& value) {
assert(0 <= v && v < _n);
assert(_vertex_node[v] != -1);
_values[v] = std::move(value);
recompute_up(_vertex_node[v]);
}
void set_edge_cost(int edge_id, T cost) {
assert(0 <= edge_id && edge_id < int(_edge_node.size()));
int node = _edge_node[edge_id];
assert(node != -1);
_nodes[node].edge.cost = cost;
recompute_up(node);
}
const Path& path_down(int node_id) const {
return node_path_down(node_id);
}
const Path& path_up(int node_id) const {
return node_path_up(node_id);
}
const Point& point(int node_id) const {
return node_point(node_id);
}
const Path& all_prod_down() const {
assert(_root_node != -1);
return path_down(_root_node);
}
const Path& all_prod_up() const {
assert(_root_node != -1);
return path_up(_root_node);
}
const Point& point_id() const {
return _point_id;
}
template <class F>
void for_each_rerooting_step(int v, F&& f) const {
assert(0 <= v && v < _n);
int cur = _vertex_node[v];
assert(cur != -1);
while (_nodes[cur].parent != -1) {
int par = _nodes[cur].parent;
const auto& p = _nodes[par];
RerootingStep step;
step.node = par;
if (p.type == node_type::Compress) {
step.edge = p.edge;
if (p.left == cur) {
step.type = step_type::CompressLower;
step.sibling = p.right;
} else {
assert(p.right == cur);
step.type = step_type::CompressUpper;
step.sibling = p.left;
}
} else if (p.type == node_type::Rake) {
if (p.left == cur) {
step.type = step_type::RakeRight;
step.sibling = p.right;
} else {
assert(p.right == cur);
step.type = step_type::RakeLeft;
step.sibling = p.left;
}
} else if (p.type == node_type::AddEdge) {
assert(p.left == cur);
step.type = step_type::AddEdge;
step.edge = p.edge;
} else {
assert(p.type == node_type::AddVertex);
assert(p.left == cur);
step.type = step_type::AddVertex;
step.vertex = p.vertex;
}
f(step);
cur = par;
}
}
std::vector<RerootingStep> rerooting_steps(int v) const {
std::vector<RerootingStep> result;
int cur = vertex_node(v);
int depth = 0;
while (_nodes[cur].parent != -1) {
cur = _nodes[cur].parent;
depth++;
}
result.reserve(depth);
for_each_rerooting_step(v, [&](const RerootingStep& step) {
result.push_back(step);
});
return result;
}
template <class Folder>
auto fold_rerooting(int v, Folder folder) const {
folder.start(v, _values[v], local_point(v));
for_each_rerooting_step(v, [&](const RerootingStep& step) {
if (step.type == step_type::CompressLower) {
folder.compress_lower(path_down(step.sibling), step.edge);
} else if (step.type == step_type::CompressUpper) {
folder.compress_upper(path_up(step.sibling), reversed_edge(step.edge));
} else if (step.type == step_type::AddEdge) {
folder.add_edge(reversed_edge(step.edge));
} else if (step.type == step_type::RakeLeft) {
folder.rake_left(point(step.sibling));
} else if (step.type == step_type::RakeRight) {
folder.rake_right(point(step.sibling));
} else {
folder.add_vertex(step.vertex, _values[step.vertex]);
}
});
return folder.result();
}
Path compress_down(const Path& upper, const Path& lower, edge_type edge) const {
return _compress_down(upper, lower, edge);
}
Path compress_up(const Path& lower, const Path& upper, edge_type edge) const {
return _compress_up(lower, upper, edge);
}
Point rake(const Point& left, const Point& right) const {
return _rake(left, right);
}
Point add_edge_down(const Path& path, edge_type edge) const {
return _add_edge_down(path, edge);
}
Point add_edge_up(const Path& path, edge_type edge) const {
return _add_edge_up(path, edge);
}
Path add_vertex(const Point& side, const Vertex& value, int vertex) const {
return _add_vertex(side, value, vertex);
}
static edge_type reverse_edge(edge_type edge) {
return reversed_edge(edge);
}
};
template <class T, class Vertex, class Point, class CompressDown, class CompressUp, class Rake, class AddEdgeDown,
class AddEdgeUp, class AddVertex>
RerootingStaticTopTree(const m1une::graph::Graph<T>&, const std::vector<Vertex>&, Point, CompressDown, CompressUp,
Rake, AddEdgeDown, AddEdgeUp, AddVertex, int)
-> RerootingStaticTopTree<T, Vertex, std::invoke_result_t<AddVertex, Point, Vertex, int>, Point, CompressDown,
CompressUp, Rake, AddEdgeDown, AddEdgeUp, AddVertex>;
template <class T, class Vertex, class Point, class CompressDown, class CompressUp, class Rake, class AddEdgeDown,
class AddEdgeUp, class AddVertex>
RerootingStaticTopTree(const m1une::graph::Graph<T>&, const std::vector<Vertex>&, Point, CompressDown, CompressUp,
Rake, AddEdgeDown, AddEdgeUp, AddVertex)
-> RerootingStaticTopTree<T, Vertex, std::invoke_result_t<AddVertex, Point, Vertex, int>, Point, CompressDown,
CompressUp, Rake, AddEdgeDown, AddEdgeUp, AddVertex>;
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/sparse_table_lca.hpp"
#line 9 "graph/tree/sparse_table_lca.hpp"
#line 1 "ds/range_query/sparse_table.hpp"
#line 9 "ds/range_query/sparse_table.hpp"
#line 11 "ds/range_query/sparse_table.hpp"
namespace m1une {
namespace ds {
// A Sparse Table utilizing C++20 Concepts for type safety.
// It requires a Monoid struct that satisfies `m1une::monoid::IsMonoid`.
// [IMPORTANT] For O(1) range queries to work correctly, the monoid operation MUST be idempotent.
// i.e., Monoid::op(x, x) == x must hold (e.g., Min, Max, GCD, Bitwise AND/OR).
template <m1une::monoid::IsMonoid Monoid>
struct SparseTable {
using T = typename Monoid::value_type;
private:
int _n;
std::vector<std::vector<T>> _st;
public:
// Constructs an empty sparse table.
SparseTable() : _n(0) {}
// Constructs a sparse table from an existing vector in O(N log N) time.
explicit SparseTable(const std::vector<T>& v) : _n(int(v.size())) {
if (_n == 0) return;
// Compute the maximum power of 2 needed
int max_log = std::bit_width((unsigned int)_n);
_st.assign(max_log, std::vector<T>(_n));
// Initialize the base level
for (int i = 0; i < _n; i++) {
_st[0][i] = v[i];
}
// Build the sparse table
for (int k = 1; k < max_log; k++) {
for (int i = 0; i + (1 << k) <= _n; i++) {
_st[k][i] = Monoid::op(_st[k - 1][i], _st[k - 1][i + (1 << (k - 1))]);
}
}
}
explicit SparseTable(std::vector<T>&& v) : _n(int(v.size())) {
if (_n == 0) return;
int max_log = std::bit_width((unsigned int)_n);
_st.assign(max_log, std::vector<T>(_n));
for (int i = 0; i < _n; i++) {
_st[0][i] = std::move(v[i]);
}
for (int k = 1; k < max_log; k++) {
for (int i = 0; i + (1 << k) <= _n; i++) {
_st[k][i] = Monoid::op(_st[k - 1][i], _st[k - 1][i + (1 << (k - 1))]);
}
}
}
// Constructs a sparse table from a vector of a different type U.
// It automatically adapts to the Monoid's initialization requirements:
// 1. Monoid::make(val) if it exists.
// 2. Monoid::make(val, index) if the monoid requires global indices.
// 3. static_cast<T>(val) as a fallback for simple monoids.
template <typename U>
requires (!std::same_as<U, T>) && (
requires(U x) { Monoid::make(x); } ||
requires(U x, int i) { Monoid::make(x, i); } ||
std::convertible_to<U, T>
)
explicit SparseTable(const std::vector<U>& v) : _n(int(v.size())) {
if (_n == 0) return;
int max_log = std::bit_width((unsigned int)_n);
_st.assign(max_log, std::vector<T>(_n));
// Compile-time branching based on the available make() signature
for (int i = 0; i < _n; i++) {
if constexpr (requires(U x) { Monoid::make(x); }) {
_st[0][i] = Monoid::make(v[i]);
} else if constexpr (requires(U x, int idx) { Monoid::make(x, idx); }) {
_st[0][i] = Monoid::make(v[i], i);
} else {
_st[0][i] = static_cast<T>(v[i]);
}
}
for (int k = 1; k < max_log; k++) {
for (int i = 0; i + (1 << k) <= _n; i++) {
_st[k][i] = Monoid::op(_st[k - 1][i], _st[k - 1][i + (1 << (k - 1))]);
}
}
}
// Returns the product (result of the monoid operation) in the range [l, r) in O(1) time.
// Requires the monoid operation to be idempotent.
T prod(int l, int r) const {
assert(0 <= l && l <= r && r <= _n);
if (l == r) return Monoid::id();
// Calculate the largest power of 2 less than or equal to the interval length
int k = std::bit_width((unsigned int)(r - l)) - 1;
return Monoid::op(_st[k][l], _st[k][r - (1 << k)]);
}
};
} // namespace ds
} // namespace m1une
#line 12 "graph/tree/sparse_table_lca.hpp"
namespace m1une {
namespace tree {
template <class T = int>
struct SparseTableLca {
using cost_type = T;
using edge_type = m1une::graph::Edge<T>;
int root;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> depth;
std::vector<T> dist;
std::vector<int> subtree_size;
std::vector<int> tin;
std::vector<int> tout;
std::vector<int> order;
std::vector<int> first;
std::vector<int> euler;
private:
struct RmqNode {
int depth;
int vertex;
};
struct RmqMonoid {
using value_type = RmqNode;
static value_type id() {
return {std::numeric_limits<int>::max(), -1};
}
static value_type op(const value_type& a, const value_type& b) {
if (a.depth != b.depth) return a.depth < b.depth ? a : b;
return a.vertex < b.vertex ? a : b;
}
};
int _n;
m1une::ds::SparseTable<RmqMonoid> _st;
void check_vertex(int v) const {
assert(0 <= v && v < _n);
assert(first[v] != -1);
}
public:
SparseTableLca() : root(-1), _n(0) {}
explicit SparseTableLca(const m1une::graph::Graph<T>& g, int root_ = 0) {
build(g, root_);
}
void build(const m1une::graph::Graph<T>& g, int root_ = 0) {
_n = g.size();
root = _n == 0 ? -1 : root_;
parent.assign(_n, -2);
parent_edge.assign(_n, -1);
depth.assign(_n, 0);
dist.assign(_n, T(0));
subtree_size.assign(_n, 0);
tin.assign(_n, -1);
tout.assign(_n, -1);
order.clear();
order.reserve(_n);
first.assign(_n, -1);
euler.clear();
euler.reserve(std::max(0, 2 * _n - 1));
_st = m1une::ds::SparseTable<RmqMonoid>();
if (_n == 0) return;
assert(0 <= root && root < _n);
std::vector<int> it(_n, 0);
std::vector<char> visited(_n, false);
std::vector<int> stack = {root};
visited[root] = true;
parent[root] = -1;
int timer = 0;
tin[root] = timer++;
order.push_back(root);
first[root] = 0;
euler.push_back(root);
while (!stack.empty()) {
int v = stack.back();
if (it[v] < int(g[v].size())) {
const auto& e = g[v][it[v]++];
if (!e.alive) continue;
if (visited[e.to]) continue;
visited[e.to] = true;
parent[e.to] = v;
parent_edge[e.to] = e.id;
depth[e.to] = depth[v] + 1;
dist[e.to] = dist[v] + e.cost;
tin[e.to] = timer++;
order.push_back(e.to);
first[e.to] = int(euler.size());
euler.push_back(e.to);
stack.push_back(e.to);
} else {
subtree_size[v]++;
if (parent[v] != -1) subtree_size[parent[v]] += subtree_size[v];
tout[v] = timer;
stack.pop_back();
if (!stack.empty()) euler.push_back(stack.back());
}
}
std::vector<RmqNode> rmq;
rmq.reserve(euler.size());
for (int v : euler) rmq.push_back({depth[v], v});
_st = m1une::ds::SparseTable<RmqMonoid>(std::move(rmq));
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
bool is_ancestor(int u, int v) const {
check_vertex(u);
check_vertex(v);
return tin[u] <= tin[v] && tout[v] <= tout[u];
}
bool in_subtree(int v, int u) const {
return is_ancestor(u, v);
}
int lca(int u, int v) const {
check_vertex(u);
check_vertex(v);
int l = first[u], r = first[v];
if (l > r) std::swap(l, r);
return _st.prod(l, r + 1).vertex;
}
int dist_edges(int u, int v) const {
int w = lca(u, v);
return depth[u] + depth[v] - 2 * depth[w];
}
T dist_cost(int u, int v) const {
int w = lca(u, v);
return dist[u] + dist[v] - dist[w] - dist[w];
}
std::pair<int, int> subtree_range(int v) const {
check_vertex(v);
return {tin[v], tout[v]};
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/static_top_tree.hpp"
#line 10 "graph/tree/static_top_tree.hpp"
#line 12 "graph/tree/static_top_tree.hpp"
namespace m1une {
namespace tree {
namespace internal {
enum class StaticTopTreeNodeType {
Compress,
Rake,
AddEdge,
AddVertex,
};
} // namespace internal
template <class T, class Vertex, class Path, class Point, class Compress, class Rake, class AddEdge,
class AddVertex>
struct StaticTopTree {
using cost_type = T;
using vertex_type = Vertex;
using path_type = Path;
using point_type = Point;
using edge_type = m1une::graph::Edge<T>;
private:
struct Node {
internal::StaticTopTreeNodeType type;
int left = -1;
int right = -1;
int parent = -1;
int vertex = -1;
edge_type edge;
int size = 0;
int height = 1;
std::optional<Path> path;
std::optional<Point> point;
};
int _n;
int _root;
int _root_node;
Point _point_id;
Compress _compress;
Rake _rake;
AddEdge _add_edge;
AddVertex _add_vertex;
std::vector<Vertex> _values;
std::vector<Node> _nodes;
std::vector<int> _vertex_node;
std::vector<int> _edge_node;
std::vector<int> _parent;
std::vector<int> _subtree_size;
std::vector<int> _heavy;
std::vector<edge_type> _heavy_edge;
std::vector<std::vector<edge_type>> _children;
const Path& path_value(int node) const {
assert(0 <= node && node < int(_nodes.size()));
assert(_nodes[node].path.has_value());
return *_nodes[node].path;
}
const Point& point_value(int node) const {
assert(0 <= node && node < int(_nodes.size()));
assert(_nodes[node].point.has_value());
return *_nodes[node].point;
}
void set_parent(int child, int parent) {
if (child != -1) _nodes[child].parent = parent;
}
void recompute(int node) {
auto& x = _nodes[node];
if (x.type == internal::StaticTopTreeNodeType::Compress) {
x.path = _compress(path_value(x.left), path_value(x.right), x.edge);
} else if (x.type == internal::StaticTopTreeNodeType::Rake) {
x.point = _rake(point_value(x.left), point_value(x.right));
} else if (x.type == internal::StaticTopTreeNodeType::AddEdge) {
x.point = _add_edge(path_value(x.left), x.edge);
} else {
const Point& side = x.left == -1 ? _point_id : point_value(x.left);
x.path = _add_vertex(side, _values[x.vertex], x.vertex);
}
}
int new_node(Node node) {
int id = int(_nodes.size());
_nodes.push_back(std::move(node));
set_parent(_nodes[id].left, id);
set_parent(_nodes[id].right, id);
recompute(id);
return id;
}
int new_compress(int left, int right, edge_type edge) {
Node node;
node.type = internal::StaticTopTreeNodeType::Compress;
node.left = left;
node.right = right;
node.edge = edge;
node.size = _nodes[left].size + _nodes[right].size;
node.height = std::max(_nodes[left].height, _nodes[right].height) + 1;
int id = new_node(std::move(node));
if (0 <= edge.id && edge.id < int(_edge_node.size())) _edge_node[edge.id] = id;
return id;
}
int new_rake(int left, int right) {
Node node;
node.type = internal::StaticTopTreeNodeType::Rake;
node.left = left;
node.right = right;
node.size = _nodes[left].size + _nodes[right].size;
node.height = std::max(_nodes[left].height, _nodes[right].height) + 1;
return new_node(std::move(node));
}
int new_add_edge(int child, edge_type edge) {
Node node;
node.type = internal::StaticTopTreeNodeType::AddEdge;
node.left = child;
node.edge = edge;
node.size = _nodes[child].size;
node.height = _nodes[child].height + 1;
int id = new_node(std::move(node));
if (0 <= edge.id && edge.id < int(_edge_node.size())) _edge_node[edge.id] = id;
return id;
}
int new_add_vertex(int side, int vertex) {
Node node;
node.type = internal::StaticTopTreeNodeType::AddVertex;
node.left = side;
node.vertex = vertex;
node.size = 1 + (side == -1 ? 0 : _nodes[side].size);
node.height = 1 + (side == -1 ? 0 : _nodes[side].height);
int id = new_node(std::move(node));
_vertex_node[vertex] = id;
return id;
}
int weighted_split(const std::vector<int>& nodes, int l, int r) const {
int total = 0;
for (int i = l; i < r; i++) total += _nodes[nodes[i]].size;
int left_sum = 0;
for (int i = l; i + 1 < r; i++) {
left_sum += _nodes[nodes[i]].size;
if (2 * left_sum >= total) return i + 1;
}
return r - 1;
}
int build_rake(const std::vector<int>& nodes, int l, int r) {
if (l == r) return -1;
if (l + 1 == r) return nodes[l];
int m = weighted_split(nodes, l, r);
return new_rake(build_rake(nodes, l, m), build_rake(nodes, m, r));
}
int build_compress(const std::vector<int>& nodes, const std::vector<edge_type>& edges, int l, int r) {
if (l + 1 == r) return nodes[l];
int m = weighted_split(nodes, l, r);
return new_compress(build_compress(nodes, edges, l, m), build_compress(nodes, edges, m, r), edges[m - 1]);
}
int build_vertex(int v) {
std::vector<int> side_nodes;
for (const auto& e : _children[v]) {
if (e.to == _heavy[v]) continue;
int child_path = build_path(e.to);
side_nodes.push_back(new_add_edge(child_path, e));
}
return new_add_vertex(build_rake(side_nodes, 0, int(side_nodes.size())), v);
}
int build_path(int start) {
std::vector<int> path_nodes;
std::vector<edge_type> path_edges;
for (int v = start; v != -1; v = _heavy[v]) {
path_nodes.push_back(build_vertex(v));
if (_heavy[v] != -1) path_edges.push_back(_heavy_edge[v]);
}
return build_compress(path_nodes, path_edges, 0, int(path_nodes.size()));
}
void recompute_up(int node) {
while (node != -1) {
recompute(node);
node = _nodes[node].parent;
}
}
public:
StaticTopTree(const m1une::graph::Graph<T>& g, const std::vector<Vertex>& values, Point point_id,
Compress compress, Rake rake, AddEdge add_edge, AddVertex add_vertex, int root = 0)
: _n(g.size()),
_root(_n == 0 ? -1 : root),
_root_node(-1),
_point_id(std::move(point_id)),
_compress(std::move(compress)),
_rake(std::move(rake)),
_add_edge(std::move(add_edge)),
_add_vertex(std::move(add_vertex)),
_values(values) {
build(g, root);
}
void build(const m1une::graph::Graph<T>& g, int root = 0) {
_n = g.size();
_root = _n == 0 ? -1 : root;
assert(int(_values.size()) == _n);
_nodes.clear();
_vertex_node.assign(_n, -1);
_edge_node.assign(g.edge_count(), -1);
_parent.assign(_n, -2);
_subtree_size.assign(_n, 1);
_heavy.assign(_n, -1);
_heavy_edge.assign(_n, edge_type());
_children.assign(_n, {});
_root_node = -1;
if (_n == 0) return;
assert(0 <= root && root < _n);
assert(int(g.edges().size()) == _n - 1);
std::vector<int> order;
order.reserve(_n);
std::vector<int> stack = {root};
_parent[root] = -1;
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
order.push_back(v);
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (_parent[e.to] != -2) continue;
_parent[e.to] = v;
_children[v].push_back(e);
stack.push_back(e.to);
}
}
assert(int(order.size()) == _n);
for (int i = int(order.size()) - 1; i >= 0; i--) {
int v = order[i];
for (const auto& e : _children[v]) {
_subtree_size[v] += _subtree_size[e.to];
if (_heavy[v] == -1 || _subtree_size[_heavy[v]] < _subtree_size[e.to]) {
_heavy[v] = e.to;
_heavy_edge[v] = e;
}
}
}
_root_node = build_path(root);
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
int root() const {
return _root;
}
int node_count() const {
return int(_nodes.size());
}
int height() const {
return _root_node == -1 ? 0 : _nodes[_root_node].height;
}
const Vertex& get(int v) const {
assert(0 <= v && v < _n);
return _values[v];
}
const Vertex& operator[](int v) const {
return get(v);
}
void set(int v, const Vertex& value) {
assert(0 <= v && v < _n);
assert(_vertex_node[v] != -1);
_values[v] = value;
recompute_up(_vertex_node[v]);
}
void set(int v, Vertex&& value) {
assert(0 <= v && v < _n);
assert(_vertex_node[v] != -1);
_values[v] = std::move(value);
recompute_up(_vertex_node[v]);
}
void set_edge_cost(int edge_id, T cost) {
assert(0 <= edge_id && edge_id < int(_edge_node.size()));
int node = _edge_node[edge_id];
assert(node != -1);
_nodes[node].edge.cost = cost;
recompute_up(node);
}
const Path& all_prod() const {
assert(_root_node != -1);
return path_value(_root_node);
}
const Path& query() const {
return all_prod();
}
};
template <class T, class Vertex, class Point, class Compress, class Rake, class AddEdge, class AddVertex>
StaticTopTree(const m1une::graph::Graph<T>&, const std::vector<Vertex>&, Point, Compress, Rake, AddEdge,
AddVertex, int)
-> StaticTopTree<T, Vertex, std::invoke_result_t<AddVertex, Point, Vertex, int>, Point, Compress, Rake,
AddEdge, AddVertex>;
template <class T, class Vertex, class Point, class Compress, class Rake, class AddEdge, class AddVertex>
StaticTopTree(const m1une::graph::Graph<T>&, const std::vector<Vertex>&, Point, Compress, Rake, AddEdge, AddVertex)
-> StaticTopTree<T, Vertex, std::invoke_result_t<AddVertex, Point, Vertex, int>, Point, Compress, Rake,
AddEdge, AddVertex>;
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/tree.hpp"
#line 9 "graph/tree/tree.hpp"
#line 1 "graph/tree/tree_hash.hpp"
#line 9 "graph/tree/tree_hash.hpp"
#line 11 "graph/tree/tree_hash.hpp"
namespace m1une {
namespace tree {
using TreeHashValue = std::array<std::uint64_t, 2>;
class TreeHasher {
private:
static constexpr std::uint64_t mod = (std::uint64_t(1) << 61) - 1;
std::uint64_t _seed;
static std::uint64_t splitmix64(std::uint64_t x) {
x += 0x9e3779b97f4a7c15ULL;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
return x ^ (x >> 31);
}
static std::uint64_t mul_mod(std::uint64_t a, std::uint64_t b) {
__uint128_t product = static_cast<__uint128_t>(a) * b;
std::uint64_t result = std::uint64_t(product & mod) + std::uint64_t(product >> 61);
if (mod <= result) result -= mod;
return result;
}
static std::uint64_t add_mod(std::uint64_t a, std::uint64_t b) {
std::uint64_t result = a + b;
if (mod <= result) result -= mod;
return result;
}
TreeHashValue salt(int height) const {
std::uint64_t x = static_cast<std::uint64_t>(height);
std::uint64_t first = splitmix64(_seed ^ (x + 0x243f6a8885a308d3ULL));
std::uint64_t second = splitmix64(_seed ^ (x + 0x13198a2e03707344ULL));
return {first % (mod - 1) + 1, second % (mod - 1) + 1};
}
template <class T>
static std::vector<int> tree_centers(const m1une::graph::Graph<T>& g) {
int n = g.size();
if (n == 0) return {};
std::vector<int> degree(n, 0);
std::vector<int> queue;
queue.reserve(n);
long long active_arcs = 0;
for (int v = 0; v < n; v++) {
for (const auto& e : g[v]) {
if (!e.alive) continue;
degree[v]++;
active_arcs++;
}
if (degree[v] <= 1) queue.push_back(v);
}
assert(active_arcs == 2LL * (n - 1));
std::vector<char> removed(n, false);
int remaining = n;
int head = 0;
while (2 < remaining) {
int layer_end = int(queue.size());
assert(head < layer_end);
remaining -= layer_end - head;
while (head < layer_end) {
int v = queue[head++];
removed[v] = true;
for (const auto& e : g[v]) {
if (!e.alive || removed[e.to]) continue;
if (--degree[e.to] == 1) queue.push_back(e.to);
}
}
}
std::vector<int> centers;
for (int v = 0; v < n; v++) {
if (!removed[v]) centers.push_back(v);
}
assert(1 <= int(centers.size()) && int(centers.size()) <= 2);
return centers;
}
public:
explicit TreeHasher(std::uint64_t seed = 0x6a09e667f3bcc909ULL) : _seed(seed) {}
std::uint64_t seed() const {
return _seed;
}
template <class T>
std::vector<TreeHashValue> hash_subtrees(const m1une::graph::Graph<T>& g, int root = 0) const {
int n = g.size();
if (n == 0) return {};
assert(0 <= root && root < n);
std::vector<int> parent(n, -1);
std::vector<int> order;
order.reserve(n);
parent[root] = root;
order.push_back(root);
long long active_arcs = 0;
for (int v = 0; v < n; v++) {
for (const auto& e : g[v]) active_arcs += e.alive;
}
assert(active_arcs == 2LL * (n - 1));
for (int i = 0; i < int(order.size()); i++) {
int v = order[i];
for (const auto& e : g[v]) {
if (!e.alive || parent[e.to] != -1) continue;
parent[e.to] = v;
order.push_back(e.to);
}
}
assert(int(order.size()) == n);
std::vector<int> height(n, 0);
std::vector<TreeHashValue> result(n, TreeHashValue{1, 1});
for (int i = n - 1; i >= 0; i--) {
int v = order[i];
for (const auto& e : g[v]) {
if (!e.alive || parent[e.to] != v) continue;
height[v] = std::max(height[v], height[e.to] + 1);
}
TreeHashValue random = salt(height[v]);
for (const auto& e : g[v]) {
if (!e.alive || parent[e.to] != v) continue;
result[v][0] = mul_mod(result[v][0], add_mod(result[e.to][0], random[0]));
result[v][1] = mul_mod(result[v][1], add_mod(result[e.to][1], random[1]));
}
}
return result;
}
template <class T>
TreeHashValue hash_rooted(const m1une::graph::Graph<T>& g, int root = 0) const {
if (g.empty()) return {0, 0};
return hash_subtrees(g, root)[root];
}
template <class T>
std::vector<TreeHashValue> hash_unrooted(const m1une::graph::Graph<T>& g) const {
std::vector<int> centers = tree_centers(g);
std::vector<TreeHashValue> result;
result.reserve(centers.size());
for (int center : centers) result.push_back(hash_rooted(g, center));
std::sort(result.begin(), result.end());
return result;
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/virtual_tree.hpp"
#line 8 "graph/tree/virtual_tree.hpp"
#line 11 "graph/tree/virtual_tree.hpp"
namespace m1une {
namespace tree {
template <class T = int>
struct VirtualTreeResult {
std::vector<int> vertex;
std::vector<int> parent;
std::vector<int> parent_edge_count;
std::vector<T> parent_cost;
std::vector<std::vector<int>> children;
std::vector<bool> is_key;
int size() const {
return int(vertex.size());
}
bool empty() const {
return vertex.empty();
}
int edge_count() const {
return vertex.empty() ? 0 : int(vertex.size()) - 1;
}
int root() const {
return vertex.empty() ? -1 : 0;
}
int root_vertex() const {
return vertex.empty() ? -1 : vertex[0];
}
};
template <class T = int>
struct VirtualTree {
using cost_type = T;
using result_type = VirtualTreeResult<T>;
private:
SparseTableLca<T> _lca;
std::vector<int> _key;
std::vector<int> _vertices;
std::vector<int> _stack;
public:
VirtualTree() = default;
explicit VirtualTree(const m1une::graph::Graph<T>& graph, int root = 0) : _lca(graph, root) {}
void build_lca(const m1une::graph::Graph<T>& graph, int root = 0) {
_lca.build(graph, root);
}
int original_size() const {
return _lca.size();
}
const SparseTableLca<T>& lca_data() const {
return _lca;
}
result_type build(std::vector<int> key_vertices) {
result_type result;
if (key_vertices.empty()) return result;
auto by_tin = [&](int u, int v) { return _lca.tin[u] < _lca.tin[v]; };
for (int v : key_vertices) {
assert(0 <= v && v < _lca.size());
assert(_lca.tin[v] != -1);
}
std::sort(key_vertices.begin(), key_vertices.end(), by_tin);
key_vertices.erase(std::unique(key_vertices.begin(), key_vertices.end()), key_vertices.end());
_key = key_vertices;
_vertices = key_vertices;
_vertices.reserve(2 * _key.size());
for (int i = 1; i < int(_key.size()); i++) {
_vertices.push_back(_lca.lca(_key[i - 1], _key[i]));
}
std::sort(_vertices.begin(), _vertices.end(), by_tin);
_vertices.erase(std::unique(_vertices.begin(), _vertices.end()), _vertices.end());
int n = int(_vertices.size());
result.vertex = _vertices;
result.parent.assign(n, -1);
result.parent_edge_count.assign(n, 0);
result.parent_cost.assign(n, T(0));
result.children.assign(n, {});
result.is_key.assign(n, false);
int key_index = 0;
for (int i = 0; i < n; i++) {
while (key_index < int(_key.size()) && _lca.tin[_key[key_index]] < _lca.tin[_vertices[i]]) {
key_index++;
}
if (key_index < int(_key.size()) && _key[key_index] == _vertices[i]) result.is_key[i] = true;
}
_stack.clear();
_stack.reserve(n);
for (int i = 0; i < n; i++) {
while (!_stack.empty() && !_lca.is_ancestor(_vertices[_stack.back()], _vertices[i])) {
_stack.pop_back();
}
if (!_stack.empty()) {
int p = _stack.back();
result.parent[i] = p;
result.parent_edge_count[i] = _lca.depth[_vertices[i]] - _lca.depth[_vertices[p]];
result.parent_cost[i] = _lca.dist[_vertices[i]] - _lca.dist[_vertices[p]];
result.children[p].push_back(i);
}
_stack.push_back(i);
}
return result;
}
};
} // namespace tree
} // namespace m1une
#line 1 "graph/tree/zero_one_on_tree.hpp"
#line 5 "graph/tree/zero_one_on_tree.hpp"
#include <set>
#line 7 "graph/tree/zero_one_on_tree.hpp"
#line 9 "graph/tree/zero_one_on_tree.hpp"
namespace m1une {
namespace tree {
inline long long zero_one_on_tree(const std::vector<int>& parent,
const std::vector<int>& value) {
const int n = int(parent.size());
assert(int(value.size()) == n);
if (n == 0) return 0;
int root = -1;
std::vector<std::vector<int>> children(n);
for (int v = 0; v < n; v++) {
assert(value[v] == 0 || value[v] == 1);
if (parent[v] == -1) {
assert(root == -1);
root = v;
} else {
assert(0 <= parent[v] && parent[v] < n && parent[v] != v);
children[parent[v]].push_back(v);
}
}
assert(root != -1);
std::vector<int> stack(1, root);
std::vector<char> visited(n, false);
visited[root] = true;
int visited_count = 0;
while (!stack.empty()) {
const int v = stack.back();
stack.pop_back();
visited_count++;
for (int child : children[v]) {
assert(!visited[child]);
visited[child] = true;
stack.push_back(child);
}
}
assert(visited_count == n);
struct Component {
long long zeros;
long long ones;
int vertex;
};
struct Compare {
bool operator()(const Component& lhs, const Component& rhs) const {
const long long lhs_product = lhs.zeros * rhs.ones;
const long long rhs_product = rhs.zeros * lhs.ones;
if (lhs_product != rhs_product) return lhs_product < rhs_product;
return lhs.vertex < rhs.vertex;
}
};
std::vector<long long> zeros(n), ones(n);
std::vector<int> dsu(n);
std::set<Component, Compare> components;
for (int v = 0; v < n; v++) {
zeros[v] = value[v] == 0;
ones[v] = value[v] == 1;
dsu[v] = v;
if (v != root) components.insert(Component{zeros[v], ones[v], v});
}
auto leader = [&](int v) {
int result = v;
while (dsu[result] != result) result = dsu[result];
while (dsu[v] != v) {
const int next = dsu[v];
dsu[v] = result;
v = next;
}
return result;
};
long long answer = 0;
while (!components.empty()) {
auto it = components.end();
--it;
const Component child = *it;
components.erase(it);
const int p = leader(parent[child.vertex]);
if (p != root) {
const int erased = int(components.erase(Component{zeros[p], ones[p], p}));
assert(erased == 1);
}
answer += ones[p] * zeros[child.vertex];
zeros[p] += zeros[child.vertex];
ones[p] += ones[child.vertex];
dsu[child.vertex] = p;
if (p != root) components.insert(Component{zeros[p], ones[p], p});
}
return answer;
}
template <class T>
long long zero_one_on_tree(const m1une::graph::Graph<T>& graph,
const std::vector<int>& value, int root = 0) {
const int n = graph.size();
assert(int(value.size()) == n);
if (n == 0) return 0;
assert(0 <= root && root < n);
assert(int(graph.edges().size()) == n - 1);
RootedTree<T> rooted_tree(graph, root);
assert(int(rooted_tree.order.size()) == n);
return zero_one_on_tree(rooted_tree.parent, value);
}
} // namespace tree
} // namespace m1une
#line 23 "graph/tree/all.hpp"
#line 1 "graph/undirected.hpp"
#line 1 "graph/biconnected_components.hpp"
#line 6 "graph/biconnected_components.hpp"
#line 8 "graph/biconnected_components.hpp"
namespace m1une {
namespace graph {
struct BiconnectedComponentsResult {
std::vector<std::vector<int>> components;
std::vector<std::vector<int>> edge_components;
std::vector<int> component_of_edge;
std::vector<std::vector<int>> vertex_components;
std::vector<int> articulation;
std::vector<int> ord;
std::vector<int> low;
int component_count() const {
return int(components.size());
}
bool is_articulation(int vertex) const {
assert(0 <= vertex && vertex < int(vertex_components.size()));
return vertex_components[vertex].size() >= 2;
}
};
// Decomposes an undirected graph into maximal vertex-biconnected blocks.
// Every active edge belongs to exactly one block. Isolated vertices form
// singleton blocks, and articulation vertices occur in multiple blocks.
template <class T>
BiconnectedComponentsResult biconnected_components(const Graph<T>& graph) {
const int n = graph.size();
const int edge_count = graph.edge_count();
BiconnectedComponentsResult result;
result.component_of_edge.assign(edge_count, -1);
result.vertex_components.assign(n, {});
result.ord.assign(n, -1);
result.low.assign(n, -1);
std::vector<int> edge_from(edge_count, -1);
std::vector<int> edge_to(edge_count, -1);
std::vector<int> incidence_count(edge_count, 0);
std::vector<int> alive_degree(n, 0);
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
assert(0 <= edge.id && edge.id < edge_count);
alive_degree[vertex]++;
if (incidence_count[edge.id] == 0) {
edge_from[edge.id] = edge.from;
edge_to[edge.id] = edge.to;
}
incidence_count[edge.id]++;
}
}
#ifndef NDEBUG
for (int edge_id = 0; edge_id < edge_count; edge_id++) {
if (incidence_count[edge_id] == 0) continue;
assert(incidence_count[edge_id] == 2);
assert(edge_from[edge_id] != edge_to[edge_id]);
}
#endif
std::vector<int> parent(n, -1);
std::vector<int> parent_edge(n, -1);
std::vector<int> next_edge(n, 0);
std::vector<int> dfs_stack;
std::vector<int> edge_stack;
std::vector<int> vertex_mark(n, -1);
int timer = 0;
auto add_singleton = [&](int vertex) {
const int component = result.component_count();
result.components.push_back(std::vector<int>(1, vertex));
result.edge_components.emplace_back();
result.vertex_components[vertex].push_back(component);
};
auto extract_component = [&](int stopping_edge) {
const int component = result.component_count();
result.components.emplace_back();
result.edge_components.emplace_back();
std::vector<int>& vertices = result.components.back();
std::vector<int>& edges = result.edge_components.back();
while (true) {
assert(!edge_stack.empty());
const int edge_id = edge_stack.back();
edge_stack.pop_back();
edges.push_back(edge_id);
result.component_of_edge[edge_id] = component;
const int endpoints[2] = {edge_from[edge_id], edge_to[edge_id]};
for (int vertex : endpoints) {
if (vertex_mark[vertex] == component) continue;
vertex_mark[vertex] = component;
vertices.push_back(vertex);
}
if (edge_id == stopping_edge) break;
}
for (int vertex : vertices) {
result.vertex_components[vertex].push_back(component);
}
};
for (int root = 0; root < n; root++) {
if (result.ord[root] != -1) continue;
if (alive_degree[root] == 0) {
result.ord[root] = result.low[root] = timer++;
add_singleton(root);
continue;
}
result.ord[root] = result.low[root] = timer++;
dfs_stack.push_back(root);
while (!dfs_stack.empty()) {
const int vertex = dfs_stack.back();
if (next_edge[vertex] < int(graph[vertex].size())) {
const Edge<T>& edge = graph[vertex][next_edge[vertex]++];
if (!edge.alive || edge.id == parent_edge[vertex]) continue;
const int to = edge.to;
if (result.ord[to] == -1) {
parent[to] = vertex;
parent_edge[to] = edge.id;
edge_stack.push_back(edge.id);
result.ord[to] = result.low[to] = timer++;
dfs_stack.push_back(to);
} else if (result.ord[to] < result.ord[vertex]) {
edge_stack.push_back(edge.id);
if (result.ord[to] < result.low[vertex]) {
result.low[vertex] = result.ord[to];
}
}
continue;
}
dfs_stack.pop_back();
const int parent_vertex = parent[vertex];
if (parent_vertex == -1) {
assert(edge_stack.empty());
continue;
}
if (result.low[vertex] < result.low[parent_vertex]) {
result.low[parent_vertex] = result.low[vertex];
}
if (result.ord[parent_vertex] <= result.low[vertex]) {
extract_component(parent_edge[vertex]);
}
}
}
for (int vertex = 0; vertex < n; vertex++) {
if (result.is_articulation(vertex)) result.articulation.push_back(vertex);
}
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/block_cut_tree.hpp"
#line 6 "graph/block_cut_tree.hpp"
#line 8 "graph/block_cut_tree.hpp"
namespace m1une {
namespace graph {
struct BlockCutTreeResult {
std::vector<std::vector<int>> forest;
std::vector<int> node_of_block;
std::vector<int> node_of_articulation;
std::vector<int> node_of_vertex;
std::vector<int> block_of_node;
std::vector<int> articulation_of_node;
int node_count() const {
return int(forest.size());
}
int block_count() const {
return int(node_of_block.size());
}
bool is_block_node(int node) const {
assert(0 <= node && node < node_count());
return block_of_node[node] != -1;
}
bool is_articulation_node(int node) const {
assert(0 <= node && node < node_count());
return articulation_of_node[node] != -1;
}
};
// Builds the block-cut forest of a biconnected-components decomposition.
// Block nodes have IDs [0, block_count); articulation nodes follow them.
inline BlockCutTreeResult block_cut_tree(
const BiconnectedComponentsResult& biconnected
) {
const int vertex_count = int(biconnected.vertex_components.size());
const int block_count = biconnected.component_count();
BlockCutTreeResult result;
result.node_of_block.resize(block_count);
result.node_of_articulation.assign(vertex_count, -1);
result.node_of_vertex.assign(vertex_count, -1);
result.forest.resize(block_count);
result.block_of_node.resize(block_count);
result.articulation_of_node.assign(block_count, -1);
for (int block = 0; block < block_count; block++) {
result.node_of_block[block] = block;
result.block_of_node[block] = block;
}
for (int vertex = 0; vertex < vertex_count; vertex++) {
const std::vector<int>& blocks = biconnected.vertex_components[vertex];
assert(!blocks.empty());
if (blocks.size() == 1) {
assert(0 <= blocks[0] && blocks[0] < block_count);
result.node_of_vertex[vertex] = result.node_of_block[blocks[0]];
continue;
}
const int node = result.node_count();
result.node_of_articulation[vertex] = node;
result.node_of_vertex[vertex] = node;
result.forest.emplace_back();
result.block_of_node.push_back(-1);
result.articulation_of_node.push_back(vertex);
for (int block : blocks) {
assert(0 <= block && block < block_count);
const int block_node = result.node_of_block[block];
result.forest[node].push_back(block_node);
result.forest[block_node].push_back(node);
}
}
return result;
}
template <class T>
BlockCutTreeResult block_cut_tree(const Graph<T>& graph) {
return block_cut_tree(biconnected_components(graph));
}
} // namespace graph
} // namespace m1une
#line 1 "graph/chordal_graph_recognition.hpp"
#line 9 "graph/chordal_graph_recognition.hpp"
#line 11 "graph/chordal_graph_recognition.hpp"
namespace m1une {
namespace graph {
struct ChordalGraphResult {
bool is_chordal;
std::vector<int> perfect_elimination_order;
std::vector<int> induced_cycle;
};
namespace internal {
class MaximumCardinalitySearch {
std::vector<int> _head;
std::vector<int> _next;
std::vector<int> _previous;
std::vector<int> _weight;
void erase(int vertex) {
const int weight = _weight[vertex];
if (_previous[vertex] == -1) {
_head[weight] = _next[vertex];
} else {
_next[_previous[vertex]] = _next[vertex];
}
if (_next[vertex] != -1) _previous[_next[vertex]] = _previous[vertex];
}
void insert(int vertex) {
const int weight = _weight[vertex];
_previous[vertex] = -1;
_next[vertex] = _head[weight];
if (_head[weight] != -1) _previous[_head[weight]] = vertex;
_head[weight] = vertex;
}
public:
explicit MaximumCardinalitySearch(int size)
: _head(size + 1, -1),
_next(size, -1),
_previous(size, -1),
_weight(size, 0) {
for (int vertex = 0; vertex < size; vertex++) insert(vertex);
}
std::vector<int> run(const std::vector<std::vector<int>>& adjacency) {
const int size = int(adjacency.size());
std::vector<int> order;
order.reserve(size);
std::vector<char> selected(size, false);
std::vector<int> seen_neighbor(size, -1);
int maximum_weight = 0;
while (int(order.size()) < size) {
while (_head[maximum_weight] == -1) maximum_weight--;
const int vertex = _head[maximum_weight];
erase(vertex);
selected[vertex] = true;
order.push_back(vertex);
for (int to : adjacency[vertex]) {
if (to == vertex || selected[to] || seen_neighbor[to] == vertex) continue;
seen_neighbor[to] = vertex;
erase(to);
_weight[to]++;
insert(to);
maximum_weight = std::max(maximum_weight, _weight[to]);
}
}
return order;
}
};
inline std::vector<int> chordless_cycle(
const std::vector<std::vector<int>>& adjacency, int vertex, int first,
int second
) {
const int size = int(adjacency.size());
std::vector<char> forbidden(size, false);
for (int to : adjacency[vertex]) forbidden[to] = true;
forbidden[vertex] = true;
forbidden[first] = false;
forbidden[second] = false;
std::vector<int> parent(size, -1);
std::queue<int> queue;
parent[first] = first;
queue.push(first);
while (!queue.empty() && parent[second] == -1) {
const int current = queue.front();
queue.pop();
for (int to : adjacency[current]) {
if (forbidden[to] || parent[to] != -1) continue;
parent[to] = current;
queue.push(to);
}
}
assert(parent[second] != -1);
std::vector<int> path;
for (int current = second; current != first; current = parent[current]) {
path.push_back(current);
}
path.push_back(first);
std::reverse(path.begin(), path.end());
std::vector<int> cycle;
cycle.reserve(path.size() + 1);
cycle.push_back(vertex);
cycle.insert(cycle.end(), path.begin(), path.end());
return cycle;
}
} // namespace internal
// Recognizes a chordal graph. On success, returns a perfect elimination
// ordering; on failure, returns an induced cycle of length at least four.
template <class T>
ChordalGraphResult chordal_graph_recognition(const Graph<T>& graph) {
const int size = graph.size();
std::vector<std::vector<int>> adjacency(size);
for (const Edge<T>& edge : graph.edges()) {
if (edge.from == edge.to) continue;
adjacency[edge.from].push_back(edge.to);
adjacency[edge.to].push_back(edge.from);
}
std::vector<int> order = internal::MaximumCardinalitySearch(size).run(adjacency);
std::vector<int> position(size);
for (int index = 0; index < size; index++) position[order[index]] = index;
std::vector<int> parent(size, -1);
std::vector<std::vector<int>> children(size);
for (int vertex = 0; vertex < size; vertex++) {
for (int to : adjacency[vertex]) {
if (position[to] < position[vertex] &&
(parent[vertex] == -1 || position[parent[vertex]] < position[to])) {
parent[vertex] = to;
}
}
if (parent[vertex] != -1) children[parent[vertex]].push_back(vertex);
}
std::vector<int> adjacent_stamp(size, -1);
for (int center = 0; center < size; center++) {
for (int to : adjacency[center]) adjacent_stamp[to] = center;
for (int vertex : children[center]) {
for (int to : adjacency[vertex]) {
if (position[to] >= position[center] || adjacent_stamp[to] == center) continue;
return ChordalGraphResult{
false,
{},
internal::chordless_cycle(adjacency, vertex, to, center),
};
}
}
}
std::reverse(order.begin(), order.end());
return ChordalGraphResult{true, std::move(order), {}};
}
template <class T>
bool is_chordal(const Graph<T>& graph) {
return chordal_graph_recognition(graph).is_chordal;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/chromatic_number.hpp"
#line 9 "graph/chromatic_number.hpp"
#line 11 "graph/chromatic_number.hpp"
namespace m1une {
namespace graph {
namespace detail {
struct ChromaticResidues {
static constexpr std::array<std::uint32_t, 14> mod = {
1000000007, 1000000009, 998244353, 985661441, 943718401, 935329793, 918552577,
897581057, 880803841, 754974721, 645922817, 595591169, 469762049, 167772161,
};
std::array<std::uint32_t, 14> value;
explicit ChromaticResidues(std::uint32_t x = 0) {
value.fill(x);
}
void multiply(std::uint32_t x) {
for (int i = 0; i < int(mod.size()); i++) {
value[i] = std::uint32_t(std::uint64_t(value[i]) * x % mod[i]);
}
}
};
} // namespace detail
template <class T>
int chromatic_number(const Graph<T>& g) {
int n = g.size();
assert(n <= 20);
if (n == 0) return 0;
std::vector<std::uint32_t> adjacent(n, 0);
for (const auto& e : g.edges()) {
if (e.from == e.to) continue;
adjacent[e.from] |= std::uint32_t(1) << e.to;
adjacent[e.to] |= std::uint32_t(1) << e.from;
}
std::uint32_t subset_count = std::uint32_t(1) << n;
std::vector<std::uint32_t> independent_count(subset_count, 0);
independent_count[0] = 1;
for (std::uint32_t mask = 1; mask < subset_count; mask++) {
int v = std::countr_zero(mask);
std::uint32_t rest = mask ^ (std::uint32_t(1) << v);
independent_count[mask] =
independent_count[rest] + independent_count[rest & ~adjacent[v]];
}
std::vector<detail::ChromaticResidues> power(subset_count, detail::ChromaticResidues(1));
for (int colors = 1; colors <= n; colors++) {
std::array<std::uint32_t, 14> sum = {};
for (std::uint32_t mask = 0; mask < subset_count; mask++) {
power[mask].multiply(independent_count[mask]);
bool positive = ((n - std::popcount(mask)) & 1) == 0;
for (int i = 0; i < int(sum.size()); i++) {
std::uint32_t x = power[mask].value[i];
if (positive) {
sum[i] += x;
if (sum[i] >= detail::ChromaticResidues::mod[i]) {
sum[i] -= detail::ChromaticResidues::mod[i];
}
} else {
sum[i] = (sum[i] >= x ? sum[i] - x
: sum[i] + detail::ChromaticResidues::mod[i] - x);
}
}
}
for (std::uint32_t x : sum) {
if (x != 0) return colors;
}
}
return n;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/complement_connected_components.hpp"
#line 6 "graph/complement_connected_components.hpp"
#line 1 "graph/connected_components.hpp"
#line 6 "graph/connected_components.hpp"
#line 1 "ds/dsu/dsu.hpp"
#line 8 "ds/dsu/dsu.hpp"
namespace m1une {
namespace ds {
struct Dsu {
private:
int _n;
// parent_or_size[i] is the parent of i if it's >= 0.
// If it's < 0, then i is a root and -parent_or_size[i] is the size of the group.
std::vector<int> parent_or_size;
// Returns {new leader, absorbed leader}. The absorbed leader is -1 when
// both vertices already belong to the same component.
std::pair<int, int> merge_leaders(int a, int b) {
int x = leader(a), y = leader(b);
if (x == y) return {x, -1};
if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
parent_or_size[x] += parent_or_size[y];
parent_or_size[y] = x;
return {x, y};
}
public:
Dsu() : _n(0) {}
explicit Dsu(int n) : _n(n), parent_or_size(n, -1) {}
// Merges the group containing 'a' with the group containing 'b'.
// Returns the leader of the merged group.
int merge(int a, int b) {
return merge_leaders(a, b).first;
}
// Invokes callback(new_leader, absorbed_leader) after an actual merge.
// Returns the leader of the merged group.
template <class Callback>
int merge(int a, int b, Callback&& callback) {
std::pair<int, int> merged = merge_leaders(a, b);
if (merged.second != -1) callback(merged.first, merged.second);
return merged.first;
}
// Returns true if 'a' and 'b' belong to the same group.
bool same(int a, int b) {
return leader(a) == leader(b);
}
// Returns the leader (representative) of the group containing 'a'.
int leader(int a) {
if (parent_or_size[a] < 0) return a;
// Path compression
return parent_or_size[a] = leader(parent_or_size[a]);
}
// Returns the size of the group containing 'a'.
int size(int a) {
return -parent_or_size[leader(a)];
}
// Returns a list of all groups, where each group is a vector of its elements.
std::vector<std::vector<int>> groups() {
std::vector<int> leader_buf(_n), group_size(_n);
for (int i = 0; i < _n; i++) {
leader_buf[i] = leader(i);
group_size[leader_buf[i]]++;
}
std::vector<std::vector<int>> result(_n);
for (int i = 0; i < _n; i++) {
result[i].reserve(group_size[i]);
}
for (int i = 0; i < _n; i++) {
result[leader_buf[i]].push_back(i);
}
result.erase(std::remove_if(result.begin(), result.end(), [&](const std::vector<int>& v) { return v.empty(); }),
result.end());
return result;
}
};
} // namespace ds
} // namespace m1une
#line 9 "graph/connected_components.hpp"
namespace m1une {
namespace graph {
struct ConnectedComponents {
int count;
std::vector<int> comp;
std::vector<std::vector<int>> groups;
bool same(int u, int v) const {
assert(0 <= u && u < int(comp.size()));
assert(0 <= v && v < int(comp.size()));
return comp[u] == comp[v];
}
};
template <class T>
ConnectedComponents connected_components(const Graph<T>& g) {
int n = g.size();
m1une::ds::Dsu dsu(n);
for (const auto& e : g.edges()) dsu.merge(e.from, e.to);
ConnectedComponents result;
result.comp.assign(n, 0);
std::vector<int> leader_to_comp(n, -1);
for (int v = 0; v < n; v++) {
int leader = dsu.leader(v);
if (leader_to_comp[leader] == -1) {
leader_to_comp[leader] = int(result.groups.size());
result.groups.push_back({});
}
int c = leader_to_comp[leader];
result.comp[v] = c;
result.groups[c].push_back(v);
}
result.count = int(result.groups.size());
return result;
}
} // namespace graph
} // namespace m1une
#line 8 "graph/complement_connected_components.hpp"
namespace m1une {
namespace graph {
// Computes connected components after complementing the underlying simple
// undirected graph, without constructing the complement graph.
template <class T>
ConnectedComponents complement_connected_components(const Graph<T>& graph) {
const int size = graph.size();
std::vector<std::vector<int>> adjacency(size);
for (const Edge<T>& edge : graph.edges()) {
if (edge.from == edge.to) continue;
adjacency[edge.from].push_back(edge.to);
adjacency[edge.to].push_back(edge.from);
}
const int sentinel = size;
std::vector<int> next(size + 1);
std::vector<int> previous(size + 1);
if (size == 0) {
next[sentinel] = previous[sentinel] = sentinel;
} else {
next[sentinel] = 0;
previous[sentinel] = size - 1;
for (int vertex = 0; vertex < size; vertex++) {
next[vertex] = (vertex + 1 == size ? sentinel : vertex + 1);
previous[vertex] = (vertex == 0 ? sentinel : vertex - 1);
}
}
auto erase = [&](int vertex) {
next[previous[vertex]] = next[vertex];
previous[next[vertex]] = previous[vertex];
};
ConnectedComponents result;
result.comp.assign(size, -1);
std::vector<int> neighbor_stamp(size, -1);
std::queue<int> queue;
while (next[sentinel] != sentinel) {
const int root = next[sentinel];
erase(root);
const int component = int(result.groups.size());
result.groups.emplace_back();
result.groups.back().push_back(root);
result.comp[root] = component;
queue.push(root);
while (!queue.empty()) {
const int vertex = queue.front();
queue.pop();
for (int to : adjacency[vertex]) neighbor_stamp[to] = vertex;
int candidate = next[sentinel];
while (candidate != sentinel) {
const int following = next[candidate];
if (neighbor_stamp[candidate] != vertex) {
erase(candidate);
result.comp[candidate] = component;
result.groups.back().push_back(candidate);
queue.push(candidate);
}
candidate = following;
}
}
}
result.count = int(result.groups.size());
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/count_four_cycles.hpp"
#line 6 "graph/count_four_cycles.hpp"
#include <tuple>
#line 9 "graph/count_four_cycles.hpp"
#line 11 "graph/count_four_cycles.hpp"
namespace m1une {
namespace graph {
namespace four_cycle_detail {
// Counts C4s containing one particular copy of each edge in a simple graph
// whose edge weights represent parallel-edge multiplicities.
inline std::vector<long long> count_simple_per_edge(
int vertex_count,
std::vector<int> first,
std::vector<int> second,
const std::vector<long long>& multiplicity
) {
const int edge_count = int(first.size());
assert(second.size() == first.size());
assert(multiplicity.size() == first.size());
std::vector<int> degree(vertex_count, 0);
for (int edge = 0; edge < edge_count; edge++) {
degree[first[edge]]++;
degree[second[edge]]++;
}
int maximum_degree = 0;
for (int value : degree) maximum_degree = std::max(maximum_degree, value);
std::vector<int> degree_start(maximum_degree + 2, 0);
for (int value : degree) degree_start[value + 1]++;
for (int value = 0; value <= maximum_degree; value++) {
degree_start[value + 1] += degree_start[value];
}
std::vector<int> cursor = degree_start;
std::vector<int> order(vertex_count);
for (int vertex = 0; vertex < vertex_count; vertex++) {
order[cursor[degree[vertex]]++] = vertex;
}
std::vector<int> rank(vertex_count);
for (int i = 0; i < vertex_count; i++) rank[order[i]] = i;
for (int edge = 0; edge < edge_count; edge++) {
first[edge] = rank[first[edge]];
second[edge] = rank[second[edge]];
if (first[edge] < second[edge]) {
std::swap(first[edge], second[edge]);
}
}
std::vector<int> start(vertex_count + 1, 0);
for (int vertex = 0; vertex < vertex_count; vertex++) {
start[vertex + 1] = start[vertex] + degree[order[vertex]];
}
std::vector<int> end = start;
std::vector<int> edge_at(2 * edge_count);
std::vector<int> to(2 * edge_count);
for (int edge = 0; edge < edge_count; edge++) {
int position = end[first[edge]]++;
edge_at[position] = edge;
to[position] = second[edge];
}
std::vector<int> downward_end = end;
for (int vertex = 0; vertex < vertex_count; vertex++) {
for (int i = start[vertex]; i < downward_end[vertex]; i++) {
int edge = edge_at[i];
int neighbor = to[i];
int position = end[neighbor]++;
edge_at[position] = edge;
to[position] = vertex;
}
}
std::vector<long long> path_count(vertex_count, 0);
std::vector<long long> result(edge_count, 0);
for (int vertex = vertex_count - 1; vertex >= 0; vertex--) {
for (int i = start[vertex]; i < end[vertex]; i++) {
int first_edge = edge_at[i];
int middle = to[i];
end[middle]--;
for (int j = start[middle]; j < end[middle]; j++) {
int second_edge = edge_at[j];
int opposite = to[j];
path_count[opposite] +=
multiplicity[first_edge] * multiplicity[second_edge];
}
}
for (int i = start[vertex]; i < end[vertex]; i++) {
int first_edge = edge_at[i];
int middle = to[i];
for (int j = start[middle]; j < end[middle]; j++) {
int second_edge = edge_at[j];
int opposite = to[j];
long long other_paths =
path_count[opposite] -
multiplicity[first_edge] * multiplicity[second_edge];
result[first_edge] +=
other_paths * multiplicity[second_edge];
result[second_edge] +=
other_paths * multiplicity[first_edge];
}
}
for (int i = start[vertex]; i < end[vertex]; i++) {
int middle = to[i];
for (int j = start[middle]; j < end[middle]; j++) {
path_count[to[j]] = 0;
}
}
}
return result;
}
} // namespace four_cycle_detail
// Returns, for every graph edge id, the number of C4 subgraphs containing it.
// Parallel active edges are distinct choices; inactive edges receive zero.
template <class T>
std::vector<long long> count_four_cycles_per_edge(const Graph<T>& graph) {
struct ActiveEdge {
int first;
int second;
int id;
};
std::vector<ActiveEdge> active_edges;
active_edges.reserve(graph.edge_count());
for (const Edge<T>& edge : graph.edges()) {
assert(edge.from != edge.to);
assert(0 <= edge.id && edge.id < graph.edge_count());
if (edge.from == edge.to) continue;
active_edges.push_back(ActiveEdge{
std::min(edge.from, edge.to),
std::max(edge.from, edge.to),
edge.id
});
}
std::sort(
active_edges.begin(),
active_edges.end(),
[](const ActiveEdge& left, const ActiveEdge& right) {
return std::tie(left.first, left.second) <
std::tie(right.first, right.second);
}
);
std::vector<int> first;
std::vector<int> second;
std::vector<long long> multiplicity;
std::vector<int> group_of_edge(graph.edge_count(), -1);
first.reserve(active_edges.size());
second.reserve(active_edges.size());
multiplicity.reserve(active_edges.size());
for (const ActiveEdge& edge : active_edges) {
if (first.empty() || first.back() != edge.first ||
second.back() != edge.second) {
first.push_back(edge.first);
second.push_back(edge.second);
multiplicity.push_back(0);
}
multiplicity.back()++;
group_of_edge[edge.id] = int(first.size()) - 1;
}
std::vector<long long> simple_result =
four_cycle_detail::count_simple_per_edge(
graph.size(),
std::move(first),
std::move(second),
multiplicity
);
std::vector<long long> result(graph.edge_count(), 0);
for (const ActiveEdge& edge : active_edges) {
result[edge.id] = simple_result[group_of_edge[edge.id]];
}
return result;
}
template <class T>
long long count_four_cycles(const Graph<T>& graph) {
std::vector<long long> per_edge = count_four_cycles_per_edge(graph);
long long incidence_count = 0;
for (long long count : per_edge) incidence_count += count;
assert(incidence_count % 4 == 0);
return incidence_count / 4;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/enumerate_cliques.hpp"
#line 8 "graph/enumerate_cliques.hpp"
#line 10 "graph/enumerate_cliques.hpp"
namespace m1une {
namespace graph {
// Invokes callback once for every nonempty clique. The callback receives a
// const reference to a temporary vector that is reused after it returns.
template <class T, class Callback>
void enumerate_cliques(const Graph<T>& graph, Callback&& callback) {
const int n = graph.size();
std::vector<std::vector<int>> adjacency(n);
for (const Edge<T>& edge : graph.edges()) {
assert(edge.from != edge.to);
if (edge.from == edge.to) continue;
adjacency[edge.from].push_back(edge.to);
adjacency[edge.to].push_back(edge.from);
}
for (std::vector<int>& neighbors : adjacency) {
std::sort(neighbors.begin(), neighbors.end());
#ifndef NDEBUG
for (int i = 1; i < int(neighbors.size()); i++) {
assert(neighbors[i - 1] != neighbors[i]);
}
#endif
neighbors.erase(
std::unique(neighbors.begin(), neighbors.end()),
neighbors.end()
);
}
int maximum_degree = 0;
std::vector<int> degree(n);
for (int vertex = 0; vertex < n; vertex++) {
degree[vertex] = int(adjacency[vertex].size());
maximum_degree = std::max(maximum_degree, degree[vertex]);
}
// Compute a degeneracy ordering in linear time. A clique is assigned to
// its first vertex in this ordering, and all its other vertices are among
// that vertex's forward neighbors.
std::vector<std::vector<int>> bucket(maximum_degree + 1);
for (int vertex = 0; vertex < n; vertex++) {
bucket[degree[vertex]].push_back(vertex);
}
std::vector<char> active(n, true);
std::vector<std::vector<int>> forward(n);
int minimum_degree = 0;
int degeneracy = 0;
for (int removed = 0; removed < n; removed++) {
while (true) {
while (bucket[minimum_degree].empty()) minimum_degree++;
int vertex = bucket[minimum_degree].back();
if (active[vertex] && degree[vertex] == minimum_degree) break;
bucket[minimum_degree].pop_back();
}
int vertex = bucket[minimum_degree].back();
bucket[minimum_degree].pop_back();
active[vertex] = false;
degeneracy = std::max(degeneracy, minimum_degree);
forward[vertex].reserve(minimum_degree);
for (int to : adjacency[vertex]) {
if (!active[to]) continue;
forward[vertex].push_back(to);
degree[to]--;
bucket[degree[to]].push_back(to);
minimum_degree = std::min(minimum_degree, degree[to]);
}
}
std::vector<int> clique;
clique.reserve(degeneracy + 1);
std::vector<std::vector<int>> candidates(degeneracy + 1);
for (int vertex = 0; vertex < n; vertex++) {
const std::vector<int>& neighbors = forward[vertex];
const int neighbor_count = int(neighbors.size());
clique.clear();
clique.push_back(vertex);
callback(std::as_const(clique));
if (neighbor_count == 0) continue;
std::vector<char> connected(
std::size_t(neighbor_count) * neighbor_count,
false
);
for (int first = 0; first < neighbor_count; first++) {
for (int second = first + 1; second < neighbor_count; second++) {
bool adjacent = std::binary_search(
adjacency[neighbors[first]].begin(),
adjacency[neighbors[first]].end(),
neighbors[second]
);
connected[std::size_t(first) * neighbor_count + second] =
adjacent;
connected[std::size_t(second) * neighbor_count + first] =
adjacent;
}
}
candidates[0].resize(neighbor_count);
for (int i = 0; i < neighbor_count; i++) candidates[0][i] = i;
auto enumerate = [&](auto&& self, int depth) -> void {
const std::vector<int>& current = candidates[depth];
for (int position = 0; position < int(current.size()); position++) {
int chosen = current[position];
clique.push_back(neighbors[chosen]);
callback(std::as_const(clique));
std::vector<int>& next = candidates[depth + 1];
next.clear();
for (int next_position = position + 1;
next_position < int(current.size());
next_position++) {
int candidate = current[next_position];
if (connected[
std::size_t(chosen) * neighbor_count + candidate
]) {
next.push_back(candidate);
}
}
if (!next.empty()) self(self, depth + 1);
clique.pop_back();
}
};
enumerate(enumerate, 0);
}
}
} // namespace graph
} // namespace m1une
#line 1 "graph/enumerate_triangles.hpp"
#line 7 "graph/enumerate_triangles.hpp"
#line 9 "graph/enumerate_triangles.hpp"
namespace m1une {
namespace graph {
template <class T, class Callback>
void enumerate_triangles(const Graph<T>& graph, Callback&& callback) {
const int n = graph.size();
const std::vector<Edge<T>> edges = graph.edges();
std::vector<int> degree(n, 0);
for (const Edge<T>& edge : edges) {
assert(edge.from != edge.to);
degree[edge.from]++;
degree[edge.to]++;
}
std::vector<std::vector<int>> oriented(n);
for (const Edge<T>& edge : edges) {
int from = edge.from;
int to = edge.to;
if (degree[from] > degree[to] ||
(degree[from] == degree[to] && from > to)) {
std::swap(from, to);
}
oriented[from].push_back(to);
}
std::vector<int> marked(n, -1);
for (int vertex = 0; vertex < n; vertex++) {
for (int to : oriented[vertex]) marked[to] = vertex;
for (int middle : oriented[vertex]) {
for (int to : oriented[middle]) {
if (marked[to] != vertex) continue;
int first = vertex;
int second = middle;
int third = to;
if (first > second) std::swap(first, second);
if (second > third) std::swap(second, third);
if (first > second) std::swap(first, second);
callback(first, second, third);
}
}
}
}
} // namespace graph
} // namespace m1une
#line 1 "graph/general_matching.hpp"
#line 9 "graph/general_matching.hpp"
#line 11 "graph/general_matching.hpp"
namespace m1une {
namespace graph {
struct GeneralMatching {
struct Edge {
int from;
int to;
int id;
bool alive;
int other(int v) const {
assert(v == from || v == to);
return from ^ to ^ v;
}
};
struct Pair {
int from;
int to;
int edge_id;
};
private:
int _n;
std::vector<Edge> _edges;
std::vector<std::vector<int>> _adj;
std::vector<int> _mate;
std::vector<int> _mate_edge;
bool _calculated;
void invalidate() {
_calculated = false;
}
void ensure_matching() {
if (!_calculated) max_matching();
}
bool is_matched_edge(int id) const {
const auto& e = _edges[id];
return _mate[e.from] == e.to && _mate_edge[e.from] == id;
}
enum MatchingLabel : char {
even_label,
odd_label,
unlabeled
};
struct MutablePartition {
std::vector<int> parent;
std::vector<int> rank;
std::vector<int> representative;
MutablePartition() = default;
explicit MutablePartition(int n) {
reset(n);
}
void reset(int n) {
parent.resize(n);
rank.assign(n, 0);
representative.resize(n);
for (int i = 0; i < n; i++) {
parent[i] = i;
representative[i] = i;
}
}
int root(int v) {
if (parent[v] == v) return v;
return parent[v] = root(parent[v]);
}
int operator()(int v) {
return representative[root(v)];
}
void unite(int a, int b) {
int ra = root(a);
int rb = root(b);
if (ra == rb) return;
if (rank[ra] < rank[rb]) std::swap(ra, rb);
parent[rb] = ra;
if (rank[ra] == rank[rb]) rank[ra]++;
}
void make_rep(int v) {
representative[root(v)] = v;
}
};
struct EdgeBucketQueue {
std::vector<std::vector<int>> bucket;
std::vector<int> head;
void reset(int n) {
bucket.assign(n + 3, {});
head.assign(n + 3, 0);
}
void insert(int edge_id, int key) {
if (key < 0 || int(bucket.size()) <= key) return;
bucket[key].push_back(edge_id);
}
int pop(int key) {
if (key < 0 || int(bucket.size()) <= key) return -1;
if (head[key] == int(bucket[key].size())) return -1;
return bucket[key][head[key]++];
}
};
struct NewMatchingPair {
int from;
int to;
int edge_id;
};
// General-graph shortest augmenting path phase solver.
struct MicaliVaziraniSolver {
GeneralMatching& graph;
int n;
int matching_size;
int delta;
int visit_token;
int even_time_token;
MutablePartition base;
MutablePartition delayed_base;
EdgeBucketQueue queue;
std::vector<MatchingLabel> label;
std::vector<MatchingLabel> h_label;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> source_bridge;
std::vector<int> target_bridge;
std::vector<int> bridge_edge;
std::vector<int> lcp;
std::vector<int> path_mark_1;
std::vector<int> path_mark_2;
std::vector<int> restore_vertex;
std::vector<int> restore_value;
std::vector<int> rep;
std::vector<int> h_mate;
std::vector<char> is_h_edge;
std::vector<std::vector<int>> contracted_into;
std::vector<int> h_parent_edge;
std::vector<int> h_even_time;
std::vector<int> h_bridge_edge;
std::vector<int> h_bridge_dir;
explicit MicaliVaziraniSolver(GeneralMatching& graph_)
: graph(graph_),
n(graph_._n),
matching_size(0),
delta(0),
visit_token(0),
even_time_token(0),
base(n),
delayed_base(n),
label(n, unlabeled),
h_label(n, unlabeled),
parent(n, -1),
parent_edge(n, -1),
source_bridge(n, -1),
target_bridge(n, -1),
bridge_edge(n, -1),
lcp(n, 0),
path_mark_1(n, 0),
path_mark_2(n, 0),
rep(n, -1),
h_mate(n, -1),
is_h_edge(graph_._edges.size(), false),
contracted_into(n),
h_parent_edge(n, -1),
h_even_time(n, 0),
h_bridge_edge(n, -1),
h_bridge_dir(n, 0) {}
bool active(int edge_id) const {
return graph._edges[edge_id].alive;
}
int other(int edge_id, int v) const {
return graph._edges[edge_id].other(v);
}
int edge_weight(int edge_id) const {
return graph.is_matched_edge(edge_id) ? 2 : 0;
}
void set_match(int edge_id) {
const auto& e = graph._edges[edge_id];
graph._mate[e.from] = e.to;
graph._mate[e.to] = e.from;
graph._mate_edge[e.from] = edge_id;
graph._mate_edge[e.to] = edge_id;
}
void initialize_greedy_matching() {
graph._mate.assign(n, -1);
graph._mate_edge.assign(n, -1);
matching_size = 0;
for (const auto& e : graph._edges) {
if (!e.alive) continue;
if (graph._mate[e.from] != -1 || graph._mate[e.to] != -1) continue;
set_match(e.id);
matching_size++;
}
}
void scan_edge(int edge_id, int from) {
if (!active(edge_id)) return;
int to = other(edge_id, from);
if (to == from || graph._mate[to] == from || label[base(to)] == odd_label) return;
if (label[to] == unlabeled) {
queue.insert(edge_id, lcp[from] + 2);
} else {
queue.insert(edge_id, (lcp[from] + lcp[to]) / 2 + 1);
}
}
void shrink_path(int blossom_base, int x, int y, int edge_id,
std::vector<std::pair<int, int>>& delayed_unions) {
int v = base(x);
while (v != blossom_base) {
base.unite(v, blossom_base);
delayed_unions.push_back({v, blossom_base});
v = graph._mate[v];
assert(v != -1);
base.unite(v, blossom_base);
delayed_unions.push_back({v, blossom_base});
base.make_rep(blossom_base);
source_bridge[v] = x;
target_bridge[v] = y;
bridge_edge[v] = edge_id;
restore_vertex.push_back(v);
restore_value.push_back(lcp[v]);
lcp[v] = lcp[x] + lcp[y] - lcp[graph._mate[v]] + 2;
for (int id : graph._adj[v]) scan_edge(id, v);
assert(parent[v] != -1);
v = base(parent[v]);
}
delayed_unions.push_back({blossom_base, blossom_base});
}
void build_phase_graph() {
std::fill(h_mate.begin(), h_mate.end(), -1);
std::fill(is_h_edge.begin(), is_h_edge.end(), false);
for (auto& vertices : contracted_into) vertices.clear();
for (int v = 0; v < n; v++) contracted_into[delayed_base(v)].push_back(v);
for (const auto& e : graph._edges) {
if (!e.alive) continue;
int u = e.from;
int v = e.to;
int uh = delayed_base(u);
int vh = delayed_base(v);
if (uh == vh) continue;
if (label[uh] == odd_label && label[vh] == odd_label) continue;
int w = edge_weight(e.id);
bool even_odd =
(label[uh] == even_label && label[vh] == odd_label && lcp[v] == lcp[u] + 1 - w) ||
(label[vh] == even_label && label[uh] == odd_label && lcp[u] == lcp[v] + 1 - w);
bool unlabeled_unlabeled = label[uh] == unlabeled && label[vh] == unlabeled && w == 2;
bool even_unlabeled =
(label[uh] == even_label && label[vh] == unlabeled && lcp[u] == delta - 2) ||
(label[vh] == even_label && label[uh] == unlabeled && lcp[v] == delta - 2);
bool even_even = label[uh] == even_label && label[vh] == even_label;
bool tight_even_even = even_even && lcp[u] + lcp[v] == 2 * delta + w - 2;
if (even_odd || unlabeled_unlabeled || even_unlabeled || tight_even_even) {
is_h_edge[e.id] = true;
if (w == 2) {
h_mate[uh] = vh;
h_mate[vh] = uh;
}
}
}
}
bool phase_one() {
delta = 0;
base.reset(n);
delayed_base.reset(n);
queue.reset(n);
std::fill(label.begin(), label.end(), unlabeled);
std::fill(parent.begin(), parent.end(), -1);
std::fill(parent_edge.begin(), parent_edge.end(), -1);
std::fill(source_bridge.begin(), source_bridge.end(), -1);
std::fill(target_bridge.begin(), target_bridge.end(), -1);
std::fill(bridge_edge.begin(), bridge_edge.end(), -1);
std::fill(lcp.begin(), lcp.end(), 0);
for (int v = 0; v < n; v++) {
if (graph._mate[v] == -1) label[v] = even_label;
}
for (int v = 0; v < n; v++) {
if (label[v] != even_label) continue;
for (int id : graph._adj[v]) scan_edge(id, v);
}
std::vector<std::pair<int, int>> delayed_unions;
while (delta <= n + 1) {
restore_vertex.clear();
restore_value.clear();
while (true) {
int edge_id = queue.pop(delta);
if (edge_id == -1) break;
if (!active(edge_id)) continue;
int x = graph._edges[edge_id].from;
int y = graph._edges[edge_id].to;
if (label[base(x)] != even_label) std::swap(x, y);
if (label[base(x)] != even_label) continue;
if (graph._mate[x] == y || base(x) == base(y) || label[base(y)] == odd_label) continue;
if (label[base(y)] == unlabeled) {
int z = graph._mate[y];
assert(z != -1);
lcp[y] = lcp[x] + 1;
lcp[z] = lcp[x] + 2;
parent[y] = x;
parent_edge[y] = edge_id;
parent[z] = y;
parent_edge[z] = graph._mate_edge[z];
label[y] = odd_label;
label[z] = even_label;
for (int id : graph._adj[z]) scan_edge(id, z);
continue;
}
if (label[base(y)] != even_label || lcp[x] + lcp[y] != 2 * delta - 2) continue;
++visit_token;
int hx = base(x);
int hy = base(y);
path_mark_1[hx] = visit_token;
path_mark_2[hy] = visit_token;
while (path_mark_1[hy] != visit_token && path_mark_2[hx] != visit_token &&
(graph._mate[hx] != -1 || graph._mate[hy] != -1)) {
if (graph._mate[hx] != -1) {
assert(parent[graph._mate[hx]] != -1);
hx = base(parent[graph._mate[hx]]);
path_mark_1[hx] = visit_token;
}
if (graph._mate[hy] != -1) {
assert(parent[graph._mate[hy]] != -1);
hy = base(parent[graph._mate[hy]]);
path_mark_2[hy] = visit_token;
}
}
if (path_mark_1[hy] == visit_token || path_mark_2[hx] == visit_token) {
int blossom_base = path_mark_1[hy] == visit_token ? hy : hx;
shrink_path(blossom_base, x, y, edge_id, delayed_unions);
shrink_path(blossom_base, y, x, edge_id, delayed_unions);
} else {
for (int i = int(restore_vertex.size()) - 1; i >= 0; i--) {
lcp[restore_vertex[i]] = restore_value[i];
}
build_phase_graph();
return true;
}
}
for (auto [a, b] : delayed_unions) {
if (a == b) {
delayed_base.make_rep(a);
} else {
delayed_base.unite(a, b);
}
}
delayed_unions.clear();
delta++;
}
return false;
}
int next_h_vertex_through_edge(int edge_id, int current_h) const {
const auto& e = graph._edges[edge_id];
return rep[rep[e.from] == current_h ? e.to : e.from];
}
int find_path_in_h(int h_vertex) {
for (int v : contracted_into[h_vertex]) {
for (int edge_id : graph._adj[v]) {
if (!is_h_edge[edge_id]) continue;
int uh = rep[other(edge_id, v)];
if (h_mate[h_vertex] == uh) continue;
if (h_label[uh] == unlabeled) {
int mate_uh = h_mate[uh];
h_label[uh] = odd_label;
h_parent_edge[uh] = edge_id;
if (mate_uh == -1) return uh;
h_label[mate_uh] = even_label;
h_even_time[mate_uh] = even_time_token++;
int found = find_path_in_h(mate_uh);
if (found != -1) return found;
} else {
int bh = delayed_base(h_vertex);
int zh = delayed_base(uh);
if (h_even_time[bh] >= h_even_time[zh]) continue;
std::vector<int> blossom_path;
std::vector<int> blossom_vertices;
while (zh != bh) {
blossom_vertices.push_back(zh);
zh = h_mate[zh];
assert(zh != -1);
blossom_vertices.push_back(zh);
blossom_path.push_back(zh);
assert(h_parent_edge[zh] != -1);
zh = delayed_base(next_h_vertex_through_edge(h_parent_edge[zh], zh));
}
for (int x : blossom_vertices) delayed_base.unite(x, bh);
delayed_base.make_rep(bh);
std::reverse(blossom_path.begin(), blossom_path.end());
for (int x : blossom_path) {
h_bridge_edge[x] = edge_id;
h_bridge_dir[x] = graph._edges[edge_id].to == v ? 1 : -1;
}
for (int x : blossom_path) {
int found = find_path_in_h(x);
if (found != -1) return found;
}
}
}
}
return -1;
}
void collect_path_in_h(std::vector<int>& path, int from_h, int to_h) {
if (from_h == to_h) return;
if (h_label[from_h] == even_label) {
int mate_from = h_mate[from_h];
assert(mate_from != -1);
int edge_id = h_parent_edge[mate_from];
assert(edge_id != -1);
path.push_back(edge_id);
collect_path_in_h(path, next_h_vertex_through_edge(edge_id, mate_from), to_h);
} else {
int edge_id = h_bridge_edge[from_h];
assert(edge_id != -1);
const auto& e = graph._edges[edge_id];
int first = rep[h_bridge_dir[from_h] == 1 ? e.from : e.to];
int second = rep[h_bridge_dir[from_h] == 1 ? e.to : e.from];
collect_path_in_h(path, first, rep[h_mate[from_h]]);
path.push_back(edge_id);
collect_path_in_h(path, second, to_h);
}
}
void add_new_pair(std::vector<NewMatchingPair>& pairs, int from, int to, int edge_id) const {
const auto& e = graph._edges[edge_id];
assert(e.alive);
assert((e.from == from && e.to == to) || (e.from == to && e.to == from));
pairs.push_back(NewMatchingPair{from, to, edge_id});
}
void collect_path_in_graph(std::vector<NewMatchingPair>& pairs, int from, int to) {
if (from == to) return;
if (label[from] == even_label) {
int mate_from = graph._mate[from];
assert(mate_from != -1);
int parent_of_mate = parent[mate_from];
int edge_id = parent_edge[mate_from];
assert(parent_of_mate != -1 && edge_id != -1);
add_new_pair(pairs, mate_from, parent_of_mate, edge_id);
collect_path_in_graph(pairs, parent_of_mate, to);
} else {
assert(source_bridge[from] != -1 && target_bridge[from] != -1 && bridge_edge[from] != -1);
collect_path_in_graph(pairs, source_bridge[from], graph._mate[from]);
add_new_pair(pairs, source_bridge[from], target_bridge[from], bridge_edge[from]);
collect_path_in_graph(pairs, target_bridge[from], to);
}
}
void augment_path(const std::vector<int>& h_path) {
std::vector<NewMatchingPair> pairs;
for (int edge_id : h_path) {
const auto& e = graph._edges[edge_id];
add_new_pair(pairs, e.from, e.to, edge_id);
collect_path_in_graph(pairs, e.from, rep[e.from]);
collect_path_in_graph(pairs, e.to, rep[e.to]);
}
for (const auto& p : pairs) {
if (graph._mate[p.from] != -1) {
int old = graph._mate[p.from];
graph._mate[old] = -1;
graph._mate_edge[old] = -1;
}
if (graph._mate[p.to] != -1) {
int old = graph._mate[p.to];
graph._mate[old] = -1;
graph._mate_edge[old] = -1;
}
graph._mate[p.from] = graph._mate[p.to] = -1;
graph._mate_edge[p.from] = graph._mate_edge[p.to] = -1;
}
for (const auto& p : pairs) {
assert(graph._mate[p.from] == -1 && graph._mate[p.to] == -1);
graph._mate[p.from] = p.to;
graph._mate[p.to] = p.from;
graph._mate_edge[p.from] = p.edge_id;
graph._mate_edge[p.to] = p.edge_id;
}
matching_size++;
}
void phase_two() {
std::fill(h_label.begin(), h_label.end(), unlabeled);
std::fill(h_parent_edge.begin(), h_parent_edge.end(), -1);
std::fill(h_bridge_edge.begin(), h_bridge_edge.end(), -1);
std::fill(h_bridge_dir.begin(), h_bridge_dir.end(), 0);
for (int v = 0; v < n; v++) rep[v] = delayed_base(v);
std::vector<std::vector<int>> paths;
for (int h_vertex = 0; h_vertex < n; h_vertex++) {
if (rep[h_vertex] != h_vertex) continue;
if (h_label[h_vertex] != unlabeled || h_mate[h_vertex] != -1) continue;
h_label[h_vertex] = even_label;
h_even_time[h_vertex] = even_time_token++;
int free_h = find_path_in_h(h_vertex);
if (free_h == -1) continue;
std::vector<int> path;
int edge_id = h_parent_edge[free_h];
assert(edge_id != -1);
path.push_back(edge_id);
collect_path_in_h(path, next_h_vertex_through_edge(edge_id, free_h), h_vertex);
paths.push_back(path);
}
assert(!paths.empty());
for (const auto& path : paths) augment_path(path);
for (auto& vertices : contracted_into) vertices.clear();
}
int solve() {
initialize_greedy_matching();
while (phase_one()) phase_two();
return matching_size;
}
};
public:
GeneralMatching() : GeneralMatching(0) {}
explicit GeneralMatching(int n) : _n(n), _adj(n), _mate(n, -1), _mate_edge(n, -1), _calculated(false) {
assert(0 <= n);
}
int size() const {
return _n;
}
int edge_count() const {
return int(_edges.size());
}
int add_edge(int from, int to) {
assert(0 <= from && from < _n);
assert(0 <= to && to < _n);
assert(from != to);
int id = int(_edges.size());
_edges.push_back(Edge{from, to, id, true});
_adj[from].push_back(id);
_adj[to].push_back(id);
invalidate();
return id;
}
Edge get_edge(int i) const {
assert(0 <= i && i < int(_edges.size()));
return _edges[i];
}
std::vector<Edge> edges(bool include_inactive = false) const {
std::vector<Edge> result;
result.reserve(_edges.size());
for (const auto& e : _edges) {
if (include_inactive || e.alive) result.push_back(e);
}
return result;
}
void set_edge_alive(int id, bool alive) {
assert(0 <= id && id < int(_edges.size()));
_edges[id].alive = alive;
invalidate();
}
void erase_edge(int id) {
set_edge_alive(id, false);
}
void revive_edge(int id) {
set_edge_alive(id, true);
}
bool is_edge_alive(int id) const {
assert(0 <= id && id < int(_edges.size()));
return _edges[id].alive;
}
int max_matching() {
MicaliVaziraniSolver solver(*this);
int result = solver.solve();
_calculated = true;
return result;
}
int matching_size() {
ensure_matching();
int result = 0;
for (int v = 0; v < _n; v++) {
if (v < _mate[v]) result++;
}
return result;
}
std::vector<int> mate() {
ensure_matching();
return _mate;
}
std::vector<int> mate_edge() {
ensure_matching();
return _mate_edge;
}
std::vector<Pair> matching() {
ensure_matching();
std::vector<Pair> result;
for (int v = 0; v < _n; v++) {
if (v < _mate[v]) result.push_back(Pair{v, _mate[v], _mate_edge[v]});
}
return result;
}
std::optional<std::vector<int>> minimum_edge_cover() {
ensure_matching();
std::vector<int> result;
std::vector<char> covered(_n, false), used_edge(_edges.size(), false);
auto use_edge = [&](int id) {
if (used_edge[id]) return;
used_edge[id] = true;
result.push_back(id);
covered[_edges[id].from] = true;
covered[_edges[id].to] = true;
};
for (int v = 0; v < _n; v++) {
if (v < _mate[v]) use_edge(_mate_edge[v]);
}
for (int v = 0; v < _n; v++) {
if (covered[v]) continue;
int id = -1;
for (int edge_id : _adj[v]) {
if (_edges[edge_id].alive) {
id = edge_id;
break;
}
}
if (id == -1) return std::nullopt;
use_edge(id);
}
return result;
}
};
struct GeneralMatchingGraph {
GeneralMatching matching;
std::vector<int> original_edge_id;
int original_edge(int edge_id) const {
assert(0 <= edge_id && edge_id < int(original_edge_id.size()));
return original_edge_id[edge_id];
}
};
template <class T>
GeneralMatchingGraph make_general_matching(const Graph<T>& g) {
GeneralMatchingGraph result;
result.matching = GeneralMatching(g.size());
for (const auto& e : g.edges()) {
int id = result.matching.add_edge(e.from, e.to);
if (int(result.original_edge_id.size()) <= id) result.original_edge_id.resize(id + 1);
result.original_edge_id[id] = e.id;
}
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/general_weighted_matching.hpp"
#line 13 "graph/general_weighted_matching.hpp"
#line 15 "graph/general_weighted_matching.hpp"
namespace m1une {
namespace graph {
namespace internal {
// Primal-dual weighted blossom algorithm using Gabow's event queues.
// Reference: H. N. Gabow, "Data Structures for Weighted Matching and
// Extensions to b-matching and f-factors", 2016.
// Vertices 1..n are atoms; larger indices represent contracted blossoms.
template <class Cost, class TotalCost>
class WeightedBlossomSolver {
public:
using cost_type = Cost;
using total_type = TotalCost;
private:
enum BlossomLabel : int {
separated_label = -2,
inner_label = -1,
free_label = 0,
outer_label = 1
};
static constexpr cost_type infinity = cost_type(1) << (sizeof(cost_type) * 8 - 2);
template <class T>
class MutableBinaryHeap {
public:
struct Node {
bool operator<(const Node& rhs) const {
if (value < rhs.value) {
return true;
}
if (rhs.value < value) {
return false;
}
return id < rhs.id;
}
T value;
int id;
};
MutableBinaryHeap() = default;
explicit MutableBinaryHeap(int capacity) : _size(0), _nodes(capacity + 1), _position(capacity, 0) {
}
bool empty() const {
return _size == 0;
}
void clear() {
while (_size > 0) {
_position[_nodes[_size].id] = 0;
_size--;
}
}
T min() const {
return _nodes[1].value;
}
int argmin() const {
return _nodes[1].id;
}
void pop() {
if (_size > 0) pop(1);
}
void erase(int id) {
if (_position[id]) pop(_position[id]);
}
bool has(int id) const {
return _position[id] != 0;
}
void update(int id, T v) {
if (!has(id)) return push(id, v);
bool up = (v < _nodes[_position[id]].value);
_nodes[_position[id]].value = v;
if (up) {
up_heap(_position[id]);
} else {
down_heap(_position[id]);
}
}
void decrease_key(int id, T v) {
if (!has(id)) return push(id, v);
if (v < _nodes[_position[id]].value) {
_nodes[_position[id]].value = v;
up_heap(_position[id]);
}
}
void push(int id, T v) {
_position[id] = ++_size;
_nodes[_size] = {v, id};
up_heap(_size);
}
private:
void pop(int pos) {
_position[_nodes[pos].id] = 0;
if (pos == _size) {
--_size;
return;
}
bool up = (_nodes[_size].value < _nodes[pos].value);
_nodes[pos] = _nodes[_size--];
_position[_nodes[pos].id] = pos;
if (up) {
up_heap(pos);
} else {
down_heap(pos);
}
}
void swap_node(int a, int b) {
std::swap(_nodes[a], _nodes[b]);
_position[_nodes[a].id] = a;
_position[_nodes[b].id] = b;
}
void down_heap(int pos) {
for (int current = pos;;) {
int next = current;
if (2 * current <= _size && _nodes[2 * current] < _nodes[next]) {
next = 2 * current;
}
if (2 * current + 1 <= _size && _nodes[2 * current + 1] < _nodes[next]) {
next = 2 * current + 1;
}
if (next == current) break;
swap_node(current, next);
current = next;
}
}
void up_heap(int pos) {
for (int current = pos; current > 1 && _nodes[current] < _nodes[current >> 1]; current >>= 1) {
swap_node(current, current >> 1);
}
}
int _size;
std::vector<Node> _nodes;
std::vector<int> _position;
};
template <class Key>
class DisjointPairingHeaps {
private:
struct Node {
Node() : key(), child(0), next(0), prev(-1) {
}
explicit Node(Key value) : key(value), child(0), next(0), prev(0) {
}
Key key;
int child;
int next;
int prev;
};
public:
DisjointPairingHeaps(int heap_count, int node_count) : _roots(heap_count), _nodes(node_count) {
}
void clear(int h) {
if (_roots[h]) {
clear_rec(_roots[h]);
_roots[h] = 0;
}
}
bool empty(int h) const {
return !_roots[h];
}
bool used(int v) const {
return _nodes[v].prev >= 0;
}
Key min(int h) const {
return _nodes[_roots[h]].key;
}
void push(int h, int v, Key key) {
_nodes[v] = Node(key);
_roots[h] = merge(_roots[h], v);
}
void erase(int h, int v) {
if (!used(v)) return;
int w = two_pass_pairing(_nodes[v].child);
if (!_nodes[v].prev) {
_roots[h] = w;
} else {
cut(v);
_roots[h] = merge(_roots[h], w);
}
_nodes[v].prev = -1;
}
void decrease_key(int h, int v, Key key) {
if (!used(v)) return push(h, v, key);
if (!_nodes[v].prev) {
_nodes[v].key = key;
} else {
cut(v);
_nodes[v].key = key;
_roots[h] = merge(_roots[h], v);
}
}
private:
void clear_rec(int v) {
for (; v; v = _nodes[v].next) {
if (_nodes[v].child) clear_rec(_nodes[v].child);
_nodes[v].prev = -1;
}
}
inline void cut(int v) {
auto& n = _nodes[v];
int previous = n.prev;
int next = n.next;
auto& previous_node = _nodes[previous];
if (previous_node.child == v) {
previous_node.child = next;
} else {
previous_node.next = next;
}
_nodes[next].prev = previous;
n.next = n.prev = 0;
}
int merge(int l, int r) {
if (!l) return r;
if (!r) return l;
if (_nodes[l].key > _nodes[r].key) std::swap(l, r);
int lc = _nodes[r].next = _nodes[l].child;
_nodes[l].child = _nodes[lc].prev = r;
return _nodes[r].prev = l;
}
int two_pass_pairing(int root) {
if (!root) return 0;
int a = root;
root = 0;
while (a) {
int b = _nodes[a].next;
int next_a = 0;
_nodes[a].prev = _nodes[a].next = 0;
if (b) {
next_a = _nodes[b].next;
_nodes[b].prev = _nodes[b].next = 0;
}
a = merge(a, b);
_nodes[a].next = root;
root = a;
a = next_a;
}
int s = _nodes[root].next;
_nodes[root].next = 0;
while (s) {
int t = _nodes[s].next;
_nodes[s].next = 0;
root = merge(root, s);
s = t;
}
return root;
}
private:
std::vector<int> _roots;
std::vector<Node> _nodes;
};
template <class T>
struct ReservablePriorityQueue : public std::priority_queue<T, std::vector<T>, std::greater<T>> {
ReservablePriorityQueue() = default;
explicit ReservablePriorityQueue(int capacity) {
this->c.reserve(capacity);
}
T min() const {
return this->top();
}
void clear() {
this->c.clear();
}
};
template <class T>
struct FixedQueue {
FixedQueue() = default;
explicit FixedQueue(int capacity) : _head(0), _tail(0), _data(capacity) {
}
void enqueue(int u) {
_data[_tail++] = u;
}
int dequeue() {
return _data[_head++];
}
bool empty() const {
return _head == _tail;
}
void clear() {
_head = _tail = 0;
}
int _head = 0;
int _tail = 0;
std::vector<T> _data;
};
public:
struct InputEdge {
int from;
int to;
cost_type cost;
};
private:
struct SolverEdge {
int to;
cost_type cost;
};
struct BlossomLink {
int from;
int to;
};
struct BlossomNode {
struct CycleLink {
int blossom;
int vertex;
};
BlossomNode() = default;
explicit BlossomNode(int vertex) : parent(0), size(1) {
cycle[0] = cycle[1] = CycleLink{vertex, vertex};
}
int next_v() const {
return cycle[0].vertex;
}
int next_b() const {
return cycle[0].blossom;
}
int prev_v() const {
return cycle[1].vertex;
}
int prev_b() const {
return cycle[1].blossom;
}
int parent = 0;
int size = 0;
CycleLink cycle[2];
};
struct VertexEvent {
VertexEvent() = default;
VertexEvent(cost_type event_time, int vertex) : time(event_time), id(vertex) {
}
bool operator<(const VertexEvent& rhs) const {
if (time < rhs.time) {
return true;
}
if (rhs.time < time) {
return false;
}
return id < rhs.id;
}
bool operator>(const VertexEvent& rhs) const {
return rhs < *this;
}
cost_type time = cost_type();
int id = 0;
};
struct EdgeEvent {
EdgeEvent() = default;
EdgeEvent(cost_type event_time, int from_, int to_) : time(event_time), from(from_), to(to_) {
}
bool operator<(const EdgeEvent& rhs) const {
if (time < rhs.time) {
return true;
}
if (time > rhs.time) {
return false;
}
return std::make_pair(from, to) < std::make_pair(rhs.from, rhs.to);
}
bool operator>(const EdgeEvent& rhs) const {
return rhs < *this;
}
cost_type time = cost_type();
int from = 0;
int to = 0;
};
public:
WeightedBlossomSolver(int n, const std::vector<InputEdge>& input_edges)
: _vertex_count(n),
_blossom_count((n - 1) / 2),
_state_count(n + _blossom_count + 1),
_offset(n + 2),
_edges(input_edges.size() * 2),
_grow_heap(_state_count),
_blossom_grow_heaps(_state_count, _state_count),
_contract_heap(int(_edges.size())),
_expand_heap(_state_count) {
for (const InputEdge& edge : input_edges) {
_offset[edge.from + 1]++;
_offset[edge.to + 1]++;
}
for (int i = 1; i <= _vertex_count + 1; i++) _offset[i] += _offset[i - 1];
for (const InputEdge& edge : input_edges) {
_edges[_offset[edge.from]++] = SolverEdge{edge.to, edge.cost * 2};
_edges[_offset[edge.to]++] = SolverEdge{edge.from, edge.cost * 2};
}
for (int i = _vertex_count + 1; i > 0; i--) _offset[i] = _offset[i - 1];
_offset[0] = 0;
}
total_type solve(std::vector<std::pair<int, int>>& matching) {
initialize_state();
initialize_potentials();
for (int vertex = 1; vertex <= _vertex_count; vertex++) {
if (_mate[vertex] == 0) augment_from(vertex);
}
matching.clear();
for (int vertex = 1; vertex <= _vertex_count; vertex++) {
if (_mate[vertex] > vertex) matching.emplace_back(vertex, _mate[vertex]);
}
return compute_matching_weight();
}
private:
total_type compute_matching_weight() const {
total_type result = 0;
for (int vertex = 1; vertex <= _vertex_count; vertex++) {
if (_mate[vertex] > vertex) {
cost_type best_cost = 0;
for (int edge_id = _offset[vertex]; edge_id < _offset[vertex + 1]; edge_id++) {
if (_edges[edge_id].to == _mate[vertex]) {
best_cost = std::max(best_cost, _edges[edge_id].cost);
}
}
result += best_cost;
}
}
return result >> 1;
}
total_type reduced_cost(int from, int to, const SolverEdge& edge) const {
return total_type(_potential[from]) + _potential[to] - edge.cost;
}
void rematch(int vertex, int new_mate) {
int old_mate = _mate[vertex];
_mate[vertex] = new_mate;
if (_mate[old_mate] != vertex) return;
if (_tree_link[vertex].to == _surface[_tree_link[vertex].to]) {
_mate[old_mate] = _tree_link[vertex].from;
rematch(_mate[old_mate], old_mate);
} else {
int from = _tree_link[vertex].from;
int to = _tree_link[vertex].to;
rematch(from, to);
rematch(to, from);
}
}
void repair_matching(int blossom) {
if (blossom <= _vertex_count) return;
int child = _base[blossom];
int first_vertex = _nodes[child].cycle[0].vertex;
int first_neighbor = _nodes[child].cycle[0].blossom;
int direction = (_nodes[first_neighbor].cycle[1].vertex == _mate[first_vertex]) ? 0 : 1;
while (true) {
int matched_vertex = _nodes[child].cycle[direction].vertex;
int matched_child = _nodes[child].cycle[direction].blossom;
if (_nodes[matched_child].cycle[1 ^ direction].vertex != _mate[matched_vertex]) break;
repair_matching(child);
repair_matching(matched_child);
child = _nodes[matched_child].cycle[direction].blossom;
}
_base[blossom] = child;
repair_matching(child);
_mate[blossom] = _mate[child];
}
void reset_clock() {
_time = 0;
_vertex_event = {infinity, 0};
}
void reset_blossom(int blossom) {
_label[blossom] = free_label;
_tree_link[blossom].from = 0;
_slack[blossom] = infinity;
_lazy[blossom] = 0;
}
void reset_search_state() {
_label[0] = free_label;
_tree_link[0].from = 0;
for (int vertex = 1; vertex <= _vertex_count; vertex++) {
if (_label[vertex] == outer_label) {
_potential[vertex] -= _time;
} else {
int blossom = _surface[vertex];
_potential[vertex] += _lazy[blossom];
if (_label[blossom] == inner_label) {
_potential[vertex] += _time - _created_at[blossom];
}
}
reset_blossom(vertex);
}
int remaining_blossoms = _blossom_count - _unused_count;
for (int blossom = _vertex_count + 1; remaining_blossoms > 0 && blossom < _state_count; blossom++) {
if (_base[blossom] != blossom) {
if (_surface[blossom] == blossom) {
repair_matching(blossom);
if (_label[blossom] == outer_label) {
_potential[blossom] += (_time - _created_at[blossom]) << 1;
} else if (_label[blossom] == inner_label) {
materialize_potential<inner_label>(blossom);
} else {
materialize_potential<free_label>(blossom);
}
}
_blossom_grow_heaps.clear(blossom);
reset_blossom(blossom);
remaining_blossoms--;
}
}
_queue.clear();
reset_clock();
_grow_heap.clear();
_contract_heap.clear();
_expand_heap.clear();
}
void augment_from(int root) {
if (_potential[root] == 0) return;
link_blossom(_surface[root], {0, 0});
make_outer(_surface[root], 0);
for (bool augmented = false; !augmented;) {
augmented = scan_tight_edges(root);
if (augmented) break;
augmented = advance_dual(root);
}
reset_search_state();
}
template <BlossomLabel target_label>
cost_type materialize_potential(int blossom) {
cost_type delta = _lazy[blossom];
_lazy[blossom] = 0;
if (target_label == inner_label) {
cost_type elapsed = _time - _created_at[blossom];
if (blossom > _vertex_count) _potential[blossom] -= elapsed << 1;
delta += elapsed;
}
return delta;
}
template <BlossomLabel target_label>
void update_grow_event(int from, int to, int to_blossom, cost_type slack) {
if (slack >= _slack[to]) return;
_slack[to] = slack;
_best_from[to] = from;
if (to == to_blossom) {
if (target_label != inner_label) {
_grow_heap.decrease_key(to, EdgeEvent(slack + _lazy[to], from, to));
}
} else {
int to_group = _group[to];
if (to_group != to) {
if (slack >= _slack[to_group]) return;
_slack[to_group] = slack;
}
_blossom_grow_heaps.decrease_key(to_blossom, to_group, EdgeEvent(slack, from, to));
if (target_label == inner_label) return;
EdgeEvent event = _blossom_grow_heaps.min(to_blossom);
_grow_heap.decrease_key(to_blossom,
EdgeEvent(event.time + _lazy[to_blossom], event.from, event.to));
}
}
void activate_grow_event(int blossom) {
if (blossom <= _vertex_count) {
if (_slack[blossom] < infinity) {
_grow_heap.push(blossom,
EdgeEvent(_slack[blossom] + _lazy[blossom], _best_from[blossom], blossom));
}
} else {
if (_blossom_grow_heaps.empty(blossom)) return;
EdgeEvent event = _blossom_grow_heaps.min(blossom);
_grow_heap.push(blossom, EdgeEvent(event.time + _lazy[blossom], event.from, event.to));
}
}
void swap_blossoms(int a, int b) {
// b is a maximal blossom.
std::swap(_base[a], _base[b]);
if (_base[a] == a) _base[a] = b;
std::swap(_heavy[a], _heavy[b]);
if (_heavy[a] == a) _heavy[a] = b;
std::swap(_tree_link[a], _tree_link[b]);
std::swap(_mate[a], _mate[b]);
std::swap(_potential[a], _potential[b]);
std::swap(_lazy[a], _lazy[b]);
std::swap(_created_at[a], _created_at[b]);
for (int direction = 0; direction < 2; direction++) {
int child = _nodes[a].cycle[direction].blossom;
_nodes[child].cycle[1 ^ direction].blossom = b;
}
std::swap(_nodes[a], _nodes[b]);
}
void assign_surface(int blossom, int surface, int group) {
_surface[blossom] = surface;
_group[blossom] = group;
if (blossom <= _vertex_count) return;
for (int child = _base[blossom]; _surface[child] != surface; child = _nodes[child].next_b()) {
assign_surface(child, surface, group);
}
}
void merge_blossom_children(int blossom) {
int largest_child = blossom;
int largest_size = 1;
int first_child = _base[blossom];
for (int child = first_child;; child = _nodes[child].next_b()) {
if (_nodes[child].size > largest_size) {
largest_size = _nodes[child].size;
largest_child = child;
}
if (_nodes[child].next_b() == first_child) break;
}
for (int child = first_child;; child = _nodes[child].next_b()) {
if (child != largest_child) assign_surface(child, largest_child, child);
if (_nodes[child].next_b() == first_child) break;
}
_group[largest_child] = largest_child;
if (largest_size > 1) {
_surface[blossom] = _heavy[blossom] = largest_child;
swap_blossoms(largest_child, blossom);
} else {
_heavy[blossom] = 0;
}
}
void contract_blossom(int x, int y, int edge_id) {
int x_blossom = _surface[x];
int y_blossom = _surface[y];
assert(x_blossom != y_blossom);
const int visit_mark = -(edge_id + 1);
_tree_link[_surface[_mate[x_blossom]]].from = visit_mark;
_tree_link[_surface[_mate[y_blossom]]].from = visit_mark;
int lca = -1;
while (true) {
if (_mate[y_blossom] != 0) std::swap(x_blossom, y_blossom);
x_blossom = lca = _surface[_tree_link[x_blossom].from];
if (_tree_link[_surface[_mate[x_blossom]]].from == visit_mark) break;
_tree_link[_surface[_mate[x_blossom]]].from = visit_mark;
}
const int blossom = _unused_blossoms[--_unused_count];
assert(_unused_count >= 0);
int tree_size = 0;
for (int direction = 0; direction < 2; direction++) {
for (int child = _surface[x]; child != lca;) {
int matched_vertex = _mate[child];
int matched_child = _surface[matched_vertex];
int vertex = _mate[matched_vertex];
int link_from = _tree_link[vertex].from;
int link_to = _tree_link[vertex].to;
tree_size += _nodes[child].size + _nodes[matched_child].size;
_tree_link[matched_vertex] = {x, y};
if (child > _vertex_count) {
_potential[child] += (_time - _created_at[child]) << 1;
}
if (matched_child > _vertex_count) _expand_heap.erase(matched_child);
make_outer(matched_child, materialize_potential<inner_label>(matched_child));
_nodes[child].cycle[direction] = {matched_child, matched_vertex};
_nodes[matched_child].cycle[1 ^ direction] = {child, vertex};
child = _surface[link_from];
_nodes[matched_child].cycle[direction] = {child, link_from};
_nodes[child].cycle[1 ^ direction] = {matched_child, link_to};
}
_nodes[_surface[x]].cycle[1 ^ direction] = {_surface[y], y};
std::swap(x, y);
}
if (lca > _vertex_count) _potential[lca] += (_time - _created_at[lca]) << 1;
_nodes[blossom].size = tree_size + _nodes[lca].size;
_base[blossom] = lca;
_tree_link[blossom] = _tree_link[lca];
_mate[blossom] = _mate[lca];
_label[blossom] = outer_label;
_surface[blossom] = blossom;
_created_at[blossom] = _time;
_potential[blossom] = 0;
_lazy[blossom] = 0;
merge_blossom_children(blossom);
}
void link_blossom(int blossom, BlossomLink link) {
_tree_link[blossom] = link;
if (blossom <= _vertex_count) return;
int first_child = _base[blossom];
link_blossom(first_child, link);
int previous_child = _nodes[first_child].prev_b();
link = {_nodes[previous_child].next_v(), _nodes[first_child].prev_v()};
for (int child = first_child;;) {
int next_child = _nodes[child].next_b();
if (next_child == first_child) break;
link_blossom(next_child, link);
BlossomLink next_link = {_nodes[next_child].prev_v(), _nodes[child].next_v()};
child = _nodes[next_child].next_b();
link_blossom(child, next_link);
}
}
void make_outer(int blossom, cost_type delta) {
_label[blossom] = outer_label;
if (blossom > _vertex_count) {
for (int child = _base[blossom]; _label[child] != outer_label; child = _nodes[child].next_b()) {
make_outer(child, delta);
}
} else {
_potential[blossom] += _time + delta;
if (_potential[blossom] < _vertex_event.time) {
_vertex_event = {_potential[blossom], blossom};
}
_queue.enqueue(blossom);
}
}
bool grow_tree(int from, int to) {
int inner_blossom = _surface[to];
bool visited = (_label[inner_blossom] != free_label);
if (!visited) link_blossom(inner_blossom, {0, 0});
_label[inner_blossom] = inner_label;
_created_at[inner_blossom] = _time;
_grow_heap.erase(inner_blossom);
if (to != inner_blossom) {
_expand_heap.update(inner_blossom, _time + (_potential[inner_blossom] >> 1));
}
int matched_vertex = _mate[inner_blossom];
if (matched_vertex == 0) {
rematch(from, to);
rematch(to, from);
return true;
}
int outer_blossom = _surface[matched_vertex];
if (!visited) {
link_blossom(outer_blossom, {from, to});
} else {
_tree_link[outer_blossom] = _tree_link[matched_vertex] = {from, to};
}
make_outer(outer_blossom, materialize_potential<free_label>(outer_blossom));
_created_at[outer_blossom] = _time;
_grow_heap.erase(outer_blossom);
return false;
}
void release_blossom(int blossom) {
_unused_blossoms[_unused_count++] = blossom;
_base[blossom] = blossom;
}
int recompute_slack(int blossom, int group) {
if (blossom <= _vertex_count) {
if (_slack[blossom] >= _slack[group]) return 0;
_slack[group] = _slack[blossom];
_best_from[group] = _best_from[blossom];
return blossom;
}
int destination = 0;
int first_child = _base[blossom];
for (int child = first_child;; child = _nodes[child].next_b()) {
int candidate = recompute_slack(child, group);
if (candidate != 0) destination = candidate;
if (_nodes[child].next_b() == first_child) break;
}
return destination;
}
void rebuild_components(int blossom, int surface, int group) {
_surface[blossom] = surface;
_group[blossom] = group;
if (blossom <= _vertex_count) return;
for (int child = _base[blossom]; _surface[child] != surface; child = _nodes[child].next_b()) {
if (child == _heavy[blossom]) {
rebuild_components(child, surface, group);
} else {
assign_surface(child, surface, child);
int destination = 0;
if (child > _vertex_count) {
_slack[child] = infinity;
destination = recompute_slack(child, child);
} else if (_slack[child] < infinity) {
destination = child;
}
if (destination > 0) {
_blossom_grow_heaps.push(surface, child,
EdgeEvent(_slack[child], _best_from[child], destination));
}
}
}
}
void promote_largest_child(int blossom) {
int largest_child = _heavy[blossom];
cost_type delta = (_time - _created_at[blossom]) + _lazy[blossom];
_lazy[blossom] = 0;
int first_child = _base[blossom];
for (int child = first_child;; child = _nodes[child].next_b()) {
_created_at[child] = _time;
_lazy[child] = delta;
if (child != largest_child) {
rebuild_components(child, child, child);
_blossom_grow_heaps.erase(blossom, child);
}
if (_nodes[child].next_b() == first_child) break;
}
if (largest_child > 0) {
swap_blossoms(largest_child, blossom);
blossom = largest_child;
}
release_blossom(blossom);
}
void expand_blossom(int blossom) {
int matched_vertex = _mate[_base[blossom]];
promote_largest_child(blossom);
BlossomLink old_link = _tree_link[matched_vertex];
int old_base = _surface[_mate[matched_vertex]];
int root = _surface[old_link.to];
int direction = (_mate[root] == _nodes[root].cycle[0].vertex) ? 1 : 0;
for (int child = _nodes[old_base].cycle[direction ^ 1].blossom; child != root;) {
_label[child] = separated_label;
activate_grow_event(child);
child = _nodes[child].cycle[direction ^ 1].blossom;
_label[child] = separated_label;
activate_grow_event(child);
child = _nodes[child].cycle[direction ^ 1].blossom;
}
for (int child = old_base;; child = _nodes[child].cycle[direction].blossom) {
_label[child] = inner_label;
int next_child = _nodes[child].cycle[direction].blossom;
if (child == root) {
_tree_link[_mate[child]] = old_link;
} else {
_tree_link[_mate[child]] = {_nodes[child].cycle[direction].vertex,
_nodes[next_child].cycle[direction ^ 1].vertex};
}
_tree_link[_surface[_mate[child]]] = _tree_link[_mate[child]];
if (child > _vertex_count) {
if (_potential[child] == 0) {
expand_blossom(child);
} else {
_expand_heap.push(child, _time + (_potential[child] >> 1));
}
}
if (child == root) break;
child = next_child;
make_outer(next_child, materialize_potential<inner_label>(next_child));
}
}
bool scan_tight_edges(int root) {
while (!_queue.empty()) {
int from = _queue.dequeue();
int from_blossom = _surface[from];
if (_potential[from] == _time) {
if (from != root) rematch(from, 0);
return true;
}
for (int edge_id = _offset[from]; edge_id < _offset[from + 1]; edge_id++) {
const SolverEdge& edge = _edges[edge_id];
int to = edge.to;
int to_blossom = _surface[to];
if (from_blossom == to_blossom) continue;
BlossomLabel to_label = _label[to_blossom];
if (to_label == outer_label) {
cost_type event_time = cost_type(reduced_cost(from, to, edge) >> 1);
if (event_time == _time) {
contract_blossom(from, to, edge_id);
from_blossom = _surface[from];
} else if (event_time < _vertex_event.time) {
_contract_heap.emplace(event_time, from, edge_id);
}
} else {
total_type event_time = reduced_cost(from, to, edge);
if (event_time >= infinity) continue;
if (to_label != inner_label) {
if (cost_type(event_time) + _lazy[to_blossom] == _time) {
if (grow_tree(from, to)) return true;
} else {
update_grow_event<free_label>(from, to, to_blossom, cost_type(event_time));
}
} else if (_mate[from] != to) {
update_grow_event<inner_label>(from, to, to_blossom, cost_type(event_time));
}
}
}
}
return false;
}
bool advance_dual(int root) {
cost_type rematch_time = _vertex_event.time;
cost_type grow_time = infinity;
if (!_grow_heap.empty()) grow_time = _grow_heap.min().time;
cost_type contract_time = infinity;
while (!_contract_heap.empty()) {
EdgeEvent event = _contract_heap.min();
int from = event.from;
int to = _edges[event.to].to;
if (_surface[from] != _surface[to]) {
contract_time = event.time;
break;
} else {
_contract_heap.pop();
}
}
cost_type expand_time = infinity;
if (!_expand_heap.empty()) expand_time = _expand_heap.min();
cost_type next_time =
std::min(std::min(rematch_time, grow_time), std::min(contract_time, expand_time));
assert(_time <= next_time && next_time < infinity);
_time = next_time;
if (_time == _vertex_event.time) {
int x = _vertex_event.id;
if (x != root) rematch(x, 0);
return true;
}
while (!_grow_heap.empty() && _grow_heap.min().time == _time) {
int from = _grow_heap.min().from;
int to = _grow_heap.min().to;
if (grow_tree(from, to)) return true;
}
while (!_contract_heap.empty() && _contract_heap.min().time == _time) {
int from = _contract_heap.min().from;
int edge_id = _contract_heap.min().to;
int to = _edges[edge_id].to;
_contract_heap.pop();
if (_surface[from] == _surface[to]) continue;
contract_blossom(from, to, edge_id);
}
while (!_expand_heap.empty() && _expand_heap.min() == _time) {
int blossom = _expand_heap.argmin();
_expand_heap.pop();
expand_blossom(blossom);
}
return false;
}
private:
void initialize_state() {
_queue = FixedQueue<int>(_vertex_count);
_mate.assign(_state_count, 0);
_tree_link.assign(_state_count, {0, 0});
_label.assign(_state_count, free_label);
_base.resize(_state_count);
for (int state = 1; state < _state_count; state++) _base[state] = state;
_surface.resize(_state_count);
for (int state = 1; state < _state_count; state++) _surface[state] = state;
_potential.resize(_state_count);
_nodes.resize(_state_count);
for (int state = 1; state < _state_count; state++) {
_nodes[state] = BlossomNode(state);
}
_unused_blossoms.resize(_blossom_count);
for (int i = 0; i < _blossom_count; i++) {
_unused_blossoms[i] = _vertex_count + _blossom_count - i;
}
_unused_count = _blossom_count;
reset_clock();
_created_at.resize(_state_count);
_slack.assign(_state_count, infinity);
_best_from.assign(_state_count, 0);
_heavy.assign(_state_count, 0);
_lazy.assign(_state_count, 0);
_group.resize(_state_count);
for (int state = 0; state < _state_count; state++) _group[state] = state;
}
void initialize_potentials() {
for (int vertex = 1; vertex <= _vertex_count; vertex++) {
cost_type maximum_cost = 0;
for (int edge_id = _offset[vertex]; edge_id < _offset[vertex + 1]; edge_id++) {
maximum_cost = std::max(maximum_cost, _edges[edge_id].cost);
}
_potential[vertex] = maximum_cost >> 1;
}
}
const int _vertex_count;
const int _blossom_count;
const int _state_count;
std::vector<int> _offset;
std::vector<SolverEdge> _edges;
FixedQueue<int> _queue;
std::vector<int> _mate;
std::vector<int> _surface;
std::vector<int> _base;
std::vector<BlossomLink> _tree_link;
std::vector<BlossomLabel> _label;
std::vector<cost_type> _potential;
std::vector<int> _unused_blossoms;
int _unused_count;
std::vector<BlossomNode> _nodes;
// Heavy children and event queues keep each search phase at O(m log n).
std::vector<int> _heavy;
std::vector<int> _group;
std::vector<cost_type> _created_at;
std::vector<cost_type> _lazy;
std::vector<cost_type> _slack;
std::vector<int> _best_from;
cost_type _time;
VertexEvent _vertex_event;
MutableBinaryHeap<EdgeEvent> _grow_heap;
DisjointPairingHeaps<EdgeEvent> _blossom_grow_heaps;
ReservablePriorityQueue<EdgeEvent> _contract_heap;
MutableBinaryHeap<cost_type> _expand_heap;
};
} // namespace internal
template <class Cost, class TotalCost = Cost>
struct GeneralWeightedMatching {
static_assert(std::is_integral_v<Cost> && std::is_signed_v<Cost>);
static_assert(std::is_integral_v<TotalCost> && std::is_signed_v<TotalCost>);
struct Edge {
int from;
int to;
Cost cost;
int id;
bool alive;
int other(int vertex) const {
assert(vertex == from || vertex == to);
return from ^ to ^ vertex;
}
};
struct Pair {
int from;
int to;
Cost cost;
int edge_id;
};
private:
int _n;
std::vector<Edge> _edges;
std::vector<std::vector<int>> _adj;
std::vector<int> _mate;
std::vector<int> _mate_edge;
TotalCost _matching_weight;
bool _calculated;
void invalidate() {
_calculated = false;
}
void ensure_matching() {
if (!_calculated) max_weight_matching();
}
public:
GeneralWeightedMatching() : GeneralWeightedMatching(0) {
}
explicit GeneralWeightedMatching(int n)
: _n(n), _adj(n), _mate(n, -1), _mate_edge(n, -1), _matching_weight(), _calculated(false) {
assert(0 <= n);
}
int size() const {
return _n;
}
int edge_count() const {
return int(_edges.size());
}
int add_edge(int from, int to, Cost cost) {
assert(0 <= from && from < _n);
assert(0 <= to && to < _n);
assert(from != to);
assert(cost <= std::numeric_limits<Cost>::max() / Cost(2));
int id = int(_edges.size());
_edges.push_back(Edge{from, to, cost, id, true});
_adj[from].push_back(id);
_adj[to].push_back(id);
invalidate();
return id;
}
Edge get_edge(int id) const {
assert(0 <= id && id < int(_edges.size()));
return _edges[id];
}
std::vector<Edge> edges(bool include_inactive = false) const {
std::vector<Edge> result;
result.reserve(_edges.size());
for (const Edge& edge : _edges) {
if (include_inactive || edge.alive) result.push_back(edge);
}
return result;
}
void set_edge_alive(int id, bool alive) {
assert(0 <= id && id < int(_edges.size()));
_edges[id].alive = alive;
invalidate();
}
void erase_edge(int id) {
set_edge_alive(id, false);
}
void revive_edge(int id) {
set_edge_alive(id, true);
}
bool is_edge_alive(int id) const {
assert(0 <= id && id < int(_edges.size()));
return _edges[id].alive;
}
TotalCost max_weight_matching() {
using Solver = internal::WeightedBlossomSolver<Cost, TotalCost>;
std::vector<typename Solver::InputEdge> input;
input.reserve(_edges.size());
for (const Edge& edge : _edges) {
if (!edge.alive || edge.cost <= Cost()) continue;
input.push_back(typename Solver::InputEdge{edge.from + 1, edge.to + 1, edge.cost});
}
Solver solver(_n, input);
std::vector<std::pair<int, int>> vertex_pairs;
solver.solve(vertex_pairs);
_mate.assign(_n, -1);
_mate_edge.assign(_n, -1);
_matching_weight = TotalCost();
for (auto [one_based_from, one_based_to] : vertex_pairs) {
int from = one_based_from - 1;
int to = one_based_to - 1;
int best_edge = -1;
for (int id : _adj[from]) {
const Edge& edge = _edges[id];
if (!edge.alive || edge.other(from) != to || edge.cost <= Cost()) continue;
if (best_edge == -1 || _edges[best_edge].cost < edge.cost) best_edge = id;
}
assert(best_edge != -1);
_mate[from] = to;
_mate[to] = from;
_mate_edge[from] = best_edge;
_mate_edge[to] = best_edge;
_matching_weight += static_cast<TotalCost>(_edges[best_edge].cost);
}
_calculated = true;
return _matching_weight;
}
TotalCost matching_weight() {
ensure_matching();
return _matching_weight;
}
int matching_size() {
ensure_matching();
int result = 0;
for (int vertex = 0; vertex < _n; vertex++) {
if (vertex < _mate[vertex]) result++;
}
return result;
}
std::vector<int> mate() {
ensure_matching();
return _mate;
}
std::vector<int> mate_edge() {
ensure_matching();
return _mate_edge;
}
std::vector<Pair> matching() {
ensure_matching();
std::vector<Pair> result;
for (int vertex = 0; vertex < _n; vertex++) {
if (vertex < _mate[vertex]) {
int id = _mate_edge[vertex];
result.push_back(Pair{vertex, _mate[vertex], _edges[id].cost, id});
}
}
return result;
}
};
template <class Cost, class TotalCost = Cost>
struct GeneralWeightedMatchingGraph {
GeneralWeightedMatching<Cost, TotalCost> matching;
std::vector<int> original_edge_id;
int original_edge(int edge_id) const {
assert(0 <= edge_id && edge_id < int(original_edge_id.size()));
return original_edge_id[edge_id];
}
};
template <class T>
GeneralWeightedMatchingGraph<T> make_general_weighted_matching(const Graph<T>& graph) {
GeneralWeightedMatchingGraph<T> result;
result.matching = GeneralWeightedMatching<T>(graph.size());
for (const auto& edge : graph.edges()) {
int id = result.matching.add_edge(edge.from, edge.to, edge.cost);
if (int(result.original_edge_id.size()) <= id) {
result.original_edge_id.resize(id + 1);
}
result.original_edge_id[id] = edge.id;
}
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/kruskal.hpp"
#line 6 "graph/kruskal.hpp"
#line 9 "graph/kruskal.hpp"
namespace m1une {
namespace graph {
template <class T>
struct MinimumSpanningForest {
T cost;
std::vector<Edge<T>> edges;
int components;
bool is_spanning_tree(int n) const {
return components <= 1 && int(edges.size()) == std::max(0, n - 1);
}
};
template <class T>
MinimumSpanningForest<T> kruskal(const Graph<T>& g) {
int n = g.size();
auto edges = g.edges();
std::sort(edges.begin(), edges.end(), [](const auto& a, const auto& b) {
return a.cost < b.cost;
});
m1une::ds::Dsu dsu(n);
MinimumSpanningForest<T> result;
result.cost = T(0);
result.components = n;
for (const auto& e : edges) {
if (dsu.same(e.from, e.to)) continue;
dsu.merge(e.from, e.to);
result.cost += e.cost;
result.edges.push_back(e);
result.components--;
}
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/lowlink.hpp"
#line 6 "graph/lowlink.hpp"
#line 8 "graph/lowlink.hpp"
namespace m1une {
namespace graph {
template <class T>
struct LowLinkResult {
std::vector<int> ord;
std::vector<int> low;
std::vector<int> articulation;
std::vector<Edge<T>> bridges;
std::vector<int> bridge_ids;
};
template <class T>
LowLinkResult<T> lowlink(const Graph<T>& g) {
int n = g.size();
LowLinkResult<T> result;
result.ord.assign(n, -1);
result.low.assign(n, -1);
int now = 0;
auto dfs = [&](auto self, int v, int parent_edge) -> void {
result.ord[v] = result.low[v] = now++;
int child_count = 0;
bool is_articulation = false;
for (const auto& e : g[v]) {
if (!e.alive) continue;
if (e.id == parent_edge) continue;
int to = e.to;
if (result.ord[to] == -1) {
child_count++;
self(self, to, e.id);
result.low[v] = std::min(result.low[v], result.low[to]);
if (parent_edge != -1 && result.ord[v] <= result.low[to]) is_articulation = true;
if (result.ord[v] < result.low[to]) {
result.bridges.push_back(e);
result.bridge_ids.push_back(e.id);
}
} else {
result.low[v] = std::min(result.low[v], result.ord[to]);
}
}
if (parent_edge == -1 && child_count >= 2) is_articulation = true;
if (is_articulation) result.articulation.push_back(v);
};
for (int v = 0; v < n; v++) {
if (result.ord[v] == -1) dfs(dfs, v, -1);
}
std::sort(result.articulation.begin(), result.articulation.end());
std::sort(result.bridge_ids.begin(), result.bridge_ids.end());
return result;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/maximum_clique.hpp"
#line 7 "graph/maximum_clique.hpp"
#line 9 "graph/maximum_clique.hpp"
namespace m1une {
namespace graph {
struct MaximumCliqueResult {
std::vector<int> vertices;
int size() const {
return int(vertices.size());
}
bool empty() const {
return vertices.empty();
}
};
struct MaximumIndependentSetResult {
std::vector<int> vertices;
int size() const {
return int(vertices.size());
}
bool empty() const {
return vertices.empty();
}
};
struct MinimumVertexCoverResult {
std::vector<int> vertices;
int size() const {
return int(vertices.size());
}
bool empty() const {
return vertices.empty();
}
};
namespace detail {
struct MaximumIndependentSetBranching {
int n;
std::vector<std::vector<char>> adjacent;
std::vector<std::vector<int>> graph;
explicit MaximumIndependentSetBranching(const std::vector<std::vector<char>>& adjacent_)
: n(int(adjacent_.size())), adjacent(adjacent_), graph(n) {
for (int v = 0; v < n; v++) {
for (int to = 0; to < n; to++) {
if (adjacent[v][to]) graph[v].push_back(to);
}
}
}
std::vector<int> solve_path(const std::vector<int>& order) const {
int m = int(order.size());
if (m == 0) return {};
std::vector<int> dp0(m, 0), dp1(m, 0);
dp1[0] = 1;
for (int i = 1; i < m; i++) {
dp0[i] = std::max(dp0[i - 1], dp1[i - 1]);
dp1[i] = dp0[i - 1] + 1;
}
std::vector<int> result;
int state = (dp1[m - 1] > dp0[m - 1] ? 1 : 0);
for (int i = m - 1; i >= 0; i--) {
if (state == 1) {
result.push_back(order[i]);
state = 0;
} else if (i > 0) {
state = (dp1[i - 1] > dp0[i - 1] ? 1 : 0);
}
}
return result;
}
std::vector<int> solve_cycle(const std::vector<int>& order) const {
int m = int(order.size());
if (m == 0) return {};
if (m == 1) return {order[0]};
std::vector<int> without_first(order.begin() + 1, order.end());
auto result_without = solve_path(without_first);
std::vector<int> result_with = {order[0]};
if (m >= 4) {
std::vector<int> middle(order.begin() + 2, order.end() - 1);
auto middle_result = solve_path(middle);
result_with.insert(result_with.end(), middle_result.begin(), middle_result.end());
}
return (result_with.size() > result_without.size() ? result_with : result_without);
}
std::vector<int> solve_degree_at_most_two(const std::vector<char>& active,
const std::vector<int>& degree) const {
std::vector<int> result;
std::vector<char> visited(n, false);
for (int s = 0; s < n; s++) {
if (!active[s] || visited[s]) continue;
std::vector<int> component;
std::vector<int> stack = {s};
visited[s] = true;
for (int it = 0; it < int(stack.size()); it++) {
int v = stack[it];
component.push_back(v);
for (int to : graph[v]) {
if (!active[to] || visited[to]) continue;
visited[to] = true;
stack.push_back(to);
}
}
if (component.size() == 1) {
result.push_back(component[0]);
continue;
}
int endpoint = -1;
for (int v : component) {
if (degree[v] <= 1) {
endpoint = v;
break;
}
}
std::vector<int> order;
if (endpoint != -1) {
int prev = -1, cur = endpoint;
while (cur != -1) {
order.push_back(cur);
int next = -1;
for (int to : graph[cur]) {
if (active[to] && to != prev) {
next = to;
break;
}
}
prev = cur;
cur = next;
}
auto part = solve_path(order);
result.insert(result.end(), part.begin(), part.end());
} else {
int start = component[0];
int first = -1;
for (int to : graph[start]) {
if (active[to]) {
first = to;
break;
}
}
assert(first != -1);
order.push_back(start);
int prev = start, cur = first;
while (cur != start) {
order.push_back(cur);
int next = -1;
for (int to : graph[cur]) {
if (active[to] && to != prev) {
next = to;
break;
}
}
assert(next != -1);
prev = cur;
cur = next;
}
auto part = solve_cycle(order);
result.insert(result.end(), part.begin(), part.end());
}
}
return result;
}
std::vector<int> solve(std::vector<char> active) const {
int active_count = 0;
int max_degree = -1;
int branch_vertex = -1;
std::vector<int> degree(n, 0);
for (int v = 0; v < n; v++) {
if (!active[v]) continue;
active_count++;
for (int to : graph[v]) {
if (active[to]) degree[v]++;
}
if (degree[v] > max_degree) {
max_degree = degree[v];
branch_vertex = v;
}
}
if (active_count == 0) return {};
if (max_degree <= 2) {
auto result = solve_degree_at_most_two(active, degree);
std::sort(result.begin(), result.end());
return result;
}
auto without = active;
without[branch_vertex] = false;
auto result_without = solve(without);
auto with = active;
with[branch_vertex] = false;
for (int to : graph[branch_vertex]) with[to] = false;
auto result_with = solve(with);
result_with.push_back(branch_vertex);
auto result = (result_with.size() > result_without.size() ? result_with : result_without);
std::sort(result.begin(), result.end());
return result;
}
std::vector<int> solve() const {
std::vector<char> active(n, true);
return solve(active);
}
};
template <class T>
std::vector<std::vector<char>> undirected_adjacency_matrix(const Graph<T>& g) {
int n = g.size();
std::vector<std::vector<char>> adjacent(n, std::vector<char>(n, false));
for (const auto& e : g.edges()) {
if (e.from == e.to) continue;
adjacent[e.from][e.to] = true;
adjacent[e.to][e.from] = true;
}
return adjacent;
}
std::vector<std::vector<char>> complement_adjacency_matrix(const std::vector<std::vector<char>>& adjacent) {
int n = int(adjacent.size());
std::vector<std::vector<char>> complement(n, std::vector<char>(n, false));
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (adjacent[i][j]) continue;
complement[i][j] = true;
complement[j][i] = true;
}
}
return complement;
}
} // namespace detail
template <class T>
bool is_clique(const Graph<T>& g, const std::vector<int>& vertices) {
auto adjacent = detail::undirected_adjacency_matrix(g);
for (int v : vertices) {
assert(0 <= v && v < g.size());
}
for (int i = 0; i < int(vertices.size()); i++) {
for (int j = i + 1; j < int(vertices.size()); j++) {
if (!adjacent[vertices[i]][vertices[j]]) return false;
}
}
return true;
}
template <class T>
bool is_independent_set(const Graph<T>& g, const std::vector<int>& vertices) {
auto adjacent = detail::undirected_adjacency_matrix(g);
for (int v : vertices) {
assert(0 <= v && v < g.size());
}
for (int i = 0; i < int(vertices.size()); i++) {
for (int j = i + 1; j < int(vertices.size()); j++) {
if (adjacent[vertices[i]][vertices[j]]) return false;
}
}
return true;
}
template <class T>
bool is_vertex_cover(const Graph<T>& g, const std::vector<int>& vertices) {
std::vector<char> selected(g.size(), false);
for (int v : vertices) {
assert(0 <= v && v < g.size());
selected[v] = true;
}
for (const auto& e : g.edges()) {
if (e.from == e.to) continue;
if (!selected[e.from] && !selected[e.to]) return false;
}
return true;
}
template <class T>
MaximumCliqueResult maximum_clique(const Graph<T>& g) {
auto adjacent = detail::undirected_adjacency_matrix(g);
auto complement = detail::complement_adjacency_matrix(adjacent);
detail::MaximumIndependentSetBranching solver(complement);
return MaximumCliqueResult{solver.solve()};
}
template <class T>
int maximum_clique_size(const Graph<T>& g) {
return maximum_clique(g).size();
}
template <class T>
MaximumIndependentSetResult maximum_independent_set(const Graph<T>& g) {
auto adjacent = detail::undirected_adjacency_matrix(g);
detail::MaximumIndependentSetBranching solver(adjacent);
return MaximumIndependentSetResult{solver.solve()};
}
template <class T>
int maximum_independent_set_size(const Graph<T>& g) {
return maximum_independent_set(g).size();
}
template <class T>
MinimumVertexCoverResult minimum_vertex_cover(const Graph<T>& g) {
auto independent = maximum_independent_set(g);
std::vector<char> in_independent(g.size(), false);
for (int v : independent.vertices) in_independent[v] = true;
MinimumVertexCoverResult result;
for (int v = 0; v < g.size(); v++) {
if (!in_independent[v]) result.vertices.push_back(v);
}
return result;
}
template <class T>
int minimum_vertex_cover_size(const Graph<T>& g) {
return minimum_vertex_cover(g).size();
}
} // namespace graph
} // namespace m1une
#line 1 "graph/minimum_steiner_tree.hpp"
#line 15 "graph/minimum_steiner_tree.hpp"
#line 17 "graph/minimum_steiner_tree.hpp"
namespace m1une {
namespace graph {
template <class Cost>
struct MinimumSteinerTreeResult {
Cost cost;
std::vector<int> edge_ids;
std::vector<int> vertices;
};
namespace internal {
inline std::vector<int> steiner_terminals(int n, std::vector<int> terminals) {
for (int v : terminals) assert(0 <= v && v < n);
std::sort(terminals.begin(), terminals.end());
terminals.erase(std::unique(terminals.begin(), terminals.end()), terminals.end());
assert(terminals.size() < std::numeric_limits<std::size_t>::digits);
return terminals;
}
template <class Cost>
struct MinimumSteinerTreeDp {
Cost cost;
Cost inf;
std::size_t states;
std::size_t width;
std::vector<Cost> dp;
std::vector<int> terminals;
};
template <class Cost, class GraphCost, class EdgeCost>
std::optional<MinimumSteinerTreeDp<Cost>> minimum_steiner_tree_dp(
const Graph<GraphCost>& g,
std::vector<int> terminals,
const std::vector<Cost>& vertex_cost,
EdgeCost edge_cost,
Cost inf
) {
const int n = g.size();
assert(vertex_cost.size() == std::size_t(n));
for (Cost cost : vertex_cost) assert(Cost(0) <= cost);
terminals = steiner_terminals(n, std::move(terminals));
const int k = int(terminals.size());
if (k == 0) return MinimumSteinerTreeDp<Cost>{Cost(0), inf, 1, std::size_t(n), {}, {}};
assert(Cost(0) < inf);
for (int v = 0; v < n; v++) {
for (const auto& edge : g[v]) {
if (edge.alive) assert(Cost(0) <= edge_cost(edge));
}
}
const std::size_t states = std::size_t(1) << k;
const std::size_t width = std::size_t(n);
assert(width <= std::numeric_limits<std::size_t>::max() / states);
std::vector<Cost> dp(states * width, inf);
for (int i = 0; i < k; i++) {
const int terminal = terminals[i];
if (vertex_cost[terminal] < inf) {
dp[(std::size_t(1) << i) * width + std::size_t(terminal)] = vertex_cost[terminal];
}
}
using QueueEntry = std::pair<Cost, int>;
for (std::size_t mask = 1; mask < states; mask++) {
const std::size_t mask_offset = mask * width;
for (std::size_t sub = (mask - 1) & mask; sub != 0; sub = (sub - 1) & mask) {
const std::size_t other = mask ^ sub;
if (sub > other) continue;
const std::size_t sub_offset = sub * width;
const std::size_t other_offset = other * width;
for (int v = 0; v < n; v++) {
const std::size_t vertex = std::size_t(v);
const Cost left = dp[sub_offset + vertex];
const Cost right = dp[other_offset + vertex];
if (left == inf || right == inf) continue;
assert(vertex_cost[v] <= right);
const Cost extra = right - vertex_cost[v];
if (left > inf - extra) continue;
const Cost candidate = left + extra;
Cost& current = dp[mask_offset + vertex];
if (candidate < current) current = candidate;
}
}
std::priority_queue<QueueEntry, std::vector<QueueEntry>, std::greater<QueueEntry>> queue;
for (int v = 0; v < n; v++) {
const Cost distance = dp[mask_offset + std::size_t(v)];
if (distance != inf) queue.emplace(distance, v);
}
while (!queue.empty()) {
auto [distance, v] = queue.top();
queue.pop();
if (distance != dp[mask_offset + std::size_t(v)]) continue;
for (const auto& edge : g[v]) {
if (!edge.alive) continue;
const Cost cost = edge_cost(edge);
if (cost >= inf || vertex_cost[edge.to] > inf - cost) continue;
const Cost extra = cost + vertex_cost[edge.to];
if (distance > inf - extra) continue;
const Cost candidate = distance + extra;
Cost& current = dp[mask_offset + std::size_t(edge.to)];
if (current <= candidate) continue;
current = candidate;
queue.emplace(candidate, edge.to);
}
}
}
const auto answer_begin = dp.begin() + (states - 1) * width;
const Cost answer = *std::min_element(answer_begin, dp.end());
if (answer == inf) return std::nullopt;
return MinimumSteinerTreeDp<Cost>{
answer,
inf,
states,
width,
std::move(dp),
std::move(terminals)
};
}
template <class T>
std::optional<MinimumSteinerTreeDp<int>> minimum_steiner_tree_unweighted_dp(
const Graph<T>& g,
std::vector<int> terminals
) {
const int n = g.size();
terminals = steiner_terminals(n, std::move(terminals));
const int k = int(terminals.size());
if (k == 0) return MinimumSteinerTreeDp<int>{0, n, 1, std::size_t(n), {}, {}};
const std::size_t states = std::size_t(1) << k;
const std::size_t width = std::size_t(n);
assert(width <= std::numeric_limits<std::size_t>::max() / states);
const int inf = n;
std::vector<int> dp(states * width, inf);
for (int i = 0; i < k; i++) {
dp[(std::size_t(1) << i) * width + std::size_t(terminals[i])] = 0;
}
for (std::size_t mask = 1; mask < states; mask++) {
const std::size_t mask_offset = mask * width;
for (std::size_t sub = (mask - 1) & mask; sub != 0; sub = (sub - 1) & mask) {
const std::size_t other = mask ^ sub;
if (sub > other) continue;
const std::size_t sub_offset = sub * width;
const std::size_t other_offset = other * width;
for (int v = 0; v < n; v++) {
const std::size_t vertex = std::size_t(v);
const int candidate = dp[sub_offset + vertex] + dp[other_offset + vertex];
int& current = dp[mask_offset + vertex];
if (candidate < current) current = candidate;
}
}
std::vector<int> bucket_head(n, -1);
std::vector<int> entry_vertex;
std::vector<int> entry_next;
entry_vertex.reserve(2 * width);
entry_next.reserve(2 * width);
auto push = [&](int distance, int v) {
entry_vertex.push_back(v);
entry_next.push_back(bucket_head[distance]);
bucket_head[distance] = int(entry_vertex.size()) - 1;
};
for (int v = 0; v < n; v++) {
const int distance = dp[mask_offset + std::size_t(v)];
if (distance != inf) push(distance, v);
}
for (int distance = 0; distance < n; distance++) {
for (int entry = bucket_head[distance]; entry != -1; entry = entry_next[entry]) {
const int v = entry_vertex[entry];
if (dp[mask_offset + std::size_t(v)] != distance) continue;
for (const auto& edge : g[v]) {
if (!edge.alive) continue;
int& current = dp[mask_offset + std::size_t(edge.to)];
if (distance + 1 >= current) continue;
current = distance + 1;
push(current, edge.to);
}
}
}
}
const auto answer_begin = dp.begin() + (states - 1) * width;
const int answer = *std::min_element(answer_begin, dp.end());
if (answer == inf) return std::nullopt;
return MinimumSteinerTreeDp<int>{
answer,
inf,
states,
width,
std::move(dp),
std::move(terminals)
};
}
template <class Cost, class GraphCost, class EdgeCost>
MinimumSteinerTreeResult<Cost> restore_minimum_steiner_tree(
const Graph<GraphCost>& g,
const MinimumSteinerTreeDp<Cost>& data,
const std::vector<Cost>& vertex_cost,
EdgeCost edge_cost
) {
MinimumSteinerTreeResult<Cost> result;
result.cost = data.cost;
if (data.terminals.empty()) return result;
const int n = g.size();
const std::size_t cells = data.states * data.width;
std::vector<char> state(cells, 0);
std::vector<char> selected_edge(g.edge_count(), false);
std::function<bool(std::size_t, int)> restore = [&](std::size_t mask, int start) {
const std::size_t position = mask * data.width + std::size_t(start);
if (state[position] == 2) return true;
if (state[position] == 1) return false;
state[position] = 1;
std::vector<int> search_parent(n, -2), search_edge(n, -1), stack;
search_parent[start] = -1;
stack.push_back(start);
int seed = -1;
std::size_t seed_split = 0;
while (!stack.empty() && seed == -1) {
const int v = stack.back();
stack.pop_back();
const std::size_t vertex_position = mask * data.width + std::size_t(v);
const Cost current = data.dp[vertex_position];
if (v != start && state[vertex_position] == 2) {
seed = v;
break;
}
if ((mask & (mask - 1)) == 0) {
const int terminal_index = int(std::countr_zero(mask));
if (v == data.terminals[terminal_index] && current == vertex_cost[v]) {
seed = v;
break;
}
}
for (std::size_t sub = (mask - 1) & mask; sub != 0; sub = (sub - 1) & mask) {
const std::size_t other = mask ^ sub;
if (sub > other) continue;
const Cost left = data.dp[sub * data.width + std::size_t(v)];
const Cost right = data.dp[other * data.width + std::size_t(v)];
if (left == data.inf || right == data.inf || right < vertex_cost[v]) continue;
const Cost extra = right - vertex_cost[v];
if (left > data.inf - extra || left + extra != current) continue;
seed = v;
seed_split = sub;
break;
}
if (seed != -1) break;
for (const auto& edge : g[v]) {
if (!edge.alive || search_parent[edge.to] != -2) continue;
const Cost cost = edge_cost(edge);
if (cost >= data.inf || vertex_cost[v] > data.inf - cost) continue;
const Cost extra = cost + vertex_cost[v];
const Cost previous = data.dp[mask * data.width + std::size_t(edge.to)];
if (previous == data.inf || previous > data.inf - extra) continue;
if (previous + extra != current) continue;
search_parent[edge.to] = v;
search_edge[edge.to] = edge.id;
stack.push_back(edge.to);
}
}
if (seed == -1) {
state[position] = 0;
return false;
}
if (seed_split != 0) {
const bool restored_left = restore(seed_split, seed);
const bool restored_right = restore(mask ^ seed_split, seed);
assert(restored_left && restored_right);
if (!restored_left || !restored_right) {
state[position] = 0;
return false;
}
}
for (int v = seed; v != -1; v = search_parent[v]) {
state[mask * data.width + std::size_t(v)] = 2;
if (search_parent[v] == -1) continue;
const int id = search_edge[v];
assert(0 <= id && id < g.edge_count());
selected_edge[id] = true;
}
return true;
};
int root = -1;
const std::size_t full_mask = data.states - 1;
for (int v = 0; v < n; v++) {
if (data.dp[full_mask * data.width + std::size_t(v)] == data.cost) {
root = v;
break;
}
}
assert(root != -1);
const bool restored = restore(full_mask, root);
assert(restored);
(void)restored;
std::vector<Edge<GraphCost>> edge_by_id(g.edge_count());
std::vector<char> has_edge(g.edge_count(), false);
for (const auto& edge : g.edges()) {
edge_by_id[edge.id] = edge;
has_edge[edge.id] = true;
}
std::vector<int> parent(n), component_size(n, 1);
for (int v = 0; v < n; v++) parent[v] = v;
auto leader = [&](auto&& self, int v) -> int {
if (parent[v] == v) return v;
return parent[v] = self(self, parent[v]);
};
std::vector<char> tree_edge(g.edge_count(), false);
for (int id = 0; id < g.edge_count(); id++) {
if (!selected_edge[id]) continue;
assert(has_edge[id]);
const auto& edge = edge_by_id[id];
int u = leader(leader, edge.from);
int v = leader(leader, edge.to);
if (u == v) continue;
if (component_size[u] < component_size[v]) std::swap(u, v);
parent[v] = u;
component_size[u] += component_size[v];
tree_edge[id] = true;
}
std::vector<std::vector<std::pair<int, int>>> tree(n);
std::vector<int> degree(n, 0);
std::vector<char> in_tree(n, false), is_terminal(n, false);
for (int terminal : data.terminals) {
in_tree[terminal] = true;
is_terminal[terminal] = true;
}
for (int id = 0; id < g.edge_count(); id++) {
if (!tree_edge[id]) continue;
const auto& edge = edge_by_id[id];
tree[edge.from].emplace_back(edge.to, id);
tree[edge.to].emplace_back(edge.from, id);
degree[edge.from]++;
degree[edge.to]++;
in_tree[edge.from] = true;
in_tree[edge.to] = true;
}
std::queue<int> leaves;
for (int v = 0; v < n; v++) {
if (in_tree[v] && !is_terminal[v] && degree[v] <= 1) leaves.push(v);
}
std::vector<char> removed_vertex(n, false), removed_edge(g.edge_count(), false);
while (!leaves.empty()) {
const int v = leaves.front();
leaves.pop();
if (removed_vertex[v] || is_terminal[v] || degree[v] > 1) continue;
removed_vertex[v] = true;
for (auto [to, id] : tree[v]) {
if (removed_edge[id]) continue;
removed_edge[id] = true;
degree[v]--;
degree[to]--;
if (!is_terminal[to] && degree[to] <= 1) leaves.push(to);
break;
}
}
Cost restored_cost = Cost(0);
for (int id = 0; id < g.edge_count(); id++) {
if (!tree_edge[id] || removed_edge[id]) continue;
result.edge_ids.push_back(id);
restored_cost += edge_cost(edge_by_id[id]);
}
for (int v = 0; v < n; v++) {
if (!in_tree[v] || removed_vertex[v]) continue;
result.vertices.push_back(v);
restored_cost += vertex_cost[v];
}
if constexpr (std::is_integral_v<Cost>) assert(restored_cost == result.cost);
result.cost = restored_cost;
return result;
}
} // namespace internal
template <class T>
std::optional<T> minimum_steiner_tree(
const Graph<T>& g,
std::vector<int> terminals,
const std::vector<T>& vertex_cost,
T inf = std::numeric_limits<T>::max() / T(4)
) {
auto result = internal::minimum_steiner_tree_dp(
g,
std::move(terminals),
vertex_cost,
[](const Edge<T>& edge) { return edge.cost; },
inf
);
if (!result) return std::nullopt;
return result->cost;
}
template <class T>
std::optional<T> minimum_steiner_tree(
const Graph<T>& g,
std::vector<int> terminals,
T inf = std::numeric_limits<T>::max() / T(4)
) {
return minimum_steiner_tree(g, std::move(terminals), std::vector<T>(g.size(), T(0)), inf);
}
template <class GraphCost, class Cost>
std::optional<Cost> minimum_steiner_tree_unweighted(
const Graph<GraphCost>& g,
std::vector<int> terminals,
const std::vector<Cost>& vertex_cost,
Cost inf = std::numeric_limits<Cost>::max() / Cost(4)
) {
auto result = internal::minimum_steiner_tree_dp(
g,
std::move(terminals),
vertex_cost,
[](const Edge<GraphCost>&) { return Cost(1); },
inf
);
if (!result) return std::nullopt;
return result->cost;
}
template <class T>
std::optional<MinimumSteinerTreeResult<T>> build_minimum_steiner_tree(
const Graph<T>& g,
std::vector<int> terminals,
const std::vector<T>& vertex_cost,
T inf = std::numeric_limits<T>::max() / T(4)
) {
auto data = internal::minimum_steiner_tree_dp(
g,
std::move(terminals),
vertex_cost,
[](const Edge<T>& edge) { return edge.cost; },
inf
);
if (!data) return std::nullopt;
return internal::restore_minimum_steiner_tree(
g,
*data,
vertex_cost,
[](const Edge<T>& edge) { return edge.cost; }
);
}
template <class T>
std::optional<MinimumSteinerTreeResult<T>> build_minimum_steiner_tree(
const Graph<T>& g,
std::vector<int> terminals,
T inf = std::numeric_limits<T>::max() / T(4)
) {
std::vector<T> vertex_cost(g.size(), T(0));
return build_minimum_steiner_tree(g, std::move(terminals), vertex_cost, inf);
}
template <class GraphCost, class Cost>
std::optional<MinimumSteinerTreeResult<Cost>> build_minimum_steiner_tree_unweighted(
const Graph<GraphCost>& g,
std::vector<int> terminals,
const std::vector<Cost>& vertex_cost,
Cost inf = std::numeric_limits<Cost>::max() / Cost(4)
) {
auto data = internal::minimum_steiner_tree_dp(
g,
std::move(terminals),
vertex_cost,
[](const Edge<GraphCost>&) { return Cost(1); },
inf
);
if (!data) return std::nullopt;
return internal::restore_minimum_steiner_tree(
g,
*data,
vertex_cost,
[](const Edge<GraphCost>&) { return Cost(1); }
);
}
template <class T>
std::optional<MinimumSteinerTreeResult<int>> build_minimum_steiner_tree_unweighted(
const Graph<T>& g,
std::vector<int> terminals
) {
auto data = internal::minimum_steiner_tree_unweighted_dp(g, std::move(terminals));
if (!data) return std::nullopt;
std::vector<int> vertex_cost(g.size(), 0);
return internal::restore_minimum_steiner_tree(
g,
*data,
vertex_cost,
[](const Edge<T>&) { return 1; }
);
}
template <class T>
std::optional<int> minimum_steiner_tree_unweighted(
const Graph<T>& g,
std::vector<int> terminals
) {
auto result = internal::minimum_steiner_tree_unweighted_dp(g, std::move(terminals));
if (!result) return std::nullopt;
return result->cost;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/namori.hpp"
#line 9 "graph/namori.hpp"
#line 11 "graph/namori.hpp"
namespace m1une {
namespace graph {
template <class T>
struct NamoriDecomposition {
int component_count;
std::vector<std::vector<int>> cycles;
std::vector<std::vector<int>> cycle_edge_ids;
std::vector<std::vector<T>> cycle_edge_costs;
std::vector<bool> on_cycle;
std::vector<int> component;
std::vector<int> cycle_root;
std::vector<int> cycle_position;
std::vector<int> parent;
std::vector<int> parent_edge;
std::vector<int> depth;
std::vector<T> dist_to_cycle;
std::vector<std::vector<int>> children;
bool same_component(int u, int v) const {
assert(0 <= u && u < int(component.size()));
assert(0 <= v && v < int(component.size()));
return component[u] == component[v];
}
bool same_tree(int u, int v) const {
assert(0 <= u && u < int(cycle_root.size()));
assert(0 <= v && v < int(cycle_root.size()));
return cycle_root[u] == cycle_root[v];
}
};
template <class T>
std::optional<NamoriDecomposition<T>> namori_decomposition(const Graph<T>& graph) {
int n = graph.size();
NamoriDecomposition<T> result;
result.component_count = 0;
result.on_cycle.assign(n, false);
result.component.assign(n, -1);
result.cycle_root.assign(n, -1);
result.cycle_position.assign(n, -1);
result.parent.assign(n, -1);
result.parent_edge.assign(n, -1);
result.depth.assign(n, 0);
result.dist_to_cycle.assign(n, T(0));
result.children.assign(n, {});
if (n == 0) return result;
std::vector<int> degree(n, 0);
for (int v = 0; v < n; v++) {
for (const auto& edge : graph[v]) {
if (edge.alive) degree[v]++;
}
}
std::queue<int> queue;
std::vector<bool> removed(n, false);
for (int v = 0; v < n; v++) {
if (degree[v] <= 1) queue.push(v);
}
while (!queue.empty()) {
int v = queue.front();
queue.pop();
if (removed[v] || degree[v] > 1) continue;
removed[v] = true;
for (const auto& edge : graph[v]) {
if (!edge.alive || removed[edge.to]) continue;
degree[edge.to]--;
if (degree[edge.to] == 1) queue.push(edge.to);
}
}
for (int v = 0; v < n; v++) {
result.on_cycle[v] = !removed[v];
}
for (int v = 0; v < n; v++) {
if (!result.on_cycle[v]) continue;
int cycle_degree = 0;
for (const auto& edge : graph[v]) {
if (edge.alive && result.on_cycle[edge.to]) cycle_degree++;
}
if (cycle_degree != 2) return std::nullopt;
}
std::vector<bool> cycle_visited(n, false);
for (int start = 0; start < n; start++) {
if (!result.on_cycle[start] || cycle_visited[start]) continue;
int component_id = int(result.cycles.size());
std::vector<int> vertices;
std::vector<int> edge_ids;
std::vector<T> edge_costs;
int current = start;
int previous_edge = -1;
while (true) {
if (cycle_visited[current]) return std::nullopt;
cycle_visited[current] = true;
vertices.push_back(current);
int next_vertex = -1;
int next_edge = -1;
T next_cost = T(0);
for (const auto& edge : graph[current]) {
if (!edge.alive || !result.on_cycle[edge.to] || edge.id == previous_edge) continue;
next_vertex = edge.to;
next_edge = edge.id;
next_cost = edge.cost;
break;
}
if (next_edge == -1) return std::nullopt;
edge_ids.push_back(next_edge);
edge_costs.push_back(next_cost);
if (next_vertex == start) break;
previous_edge = next_edge;
current = next_vertex;
if (int(vertices.size()) > n) return std::nullopt;
}
for (int position = 0; position < int(vertices.size()); position++) {
int v = vertices[position];
result.component[v] = component_id;
result.cycle_root[v] = v;
result.cycle_position[v] = position;
}
result.cycles.push_back(std::move(vertices));
result.cycle_edge_ids.push_back(std::move(edge_ids));
result.cycle_edge_costs.push_back(std::move(edge_costs));
}
if (result.cycles.empty()) return std::nullopt;
std::vector<int> stack;
stack.reserve(n);
for (const auto& cycle : result.cycles) {
for (int v : cycle) stack.push_back(v);
}
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
for (const auto& edge : graph[v]) {
if (!edge.alive || result.on_cycle[edge.to] || edge.id == result.parent_edge[v]) continue;
int to = edge.to;
if (result.component[to] != -1) continue;
result.component[to] = result.component[v];
result.cycle_root[to] = result.cycle_root[v];
result.cycle_position[to] = result.cycle_position[v];
result.parent[to] = v;
result.parent_edge[to] = edge.id;
result.depth[to] = result.depth[v] + 1;
result.dist_to_cycle[to] = result.dist_to_cycle[v] + edge.cost;
result.children[v].push_back(to);
stack.push_back(to);
}
}
for (int v = 0; v < n; v++) {
if (result.component[v] == -1) return std::nullopt;
}
result.component_count = int(result.cycles.size());
return result;
}
template <class T>
std::optional<NamoriDecomposition<T>> decompose_namori(const Graph<T>& graph) {
return namori_decomposition(graph);
}
} // namespace graph
} // namespace m1une
#line 1 "graph/st_numbering.hpp"
#line 6 "graph/st_numbering.hpp"
#line 8 "graph/st_numbering.hpp"
namespace m1une {
namespace graph {
// Returns ranks p with p[source] = 0 and p[sink] = n - 1 such that every
// other vertex has neighbors of both smaller and larger rank. Returns an empty
// vector when no such numbering exists.
template <class T>
std::vector<int> st_numbering(
const Graph<T>& graph,
int source,
int sink
) {
const int n = graph.size();
assert(0 < n);
assert(0 <= source && source < n);
assert(0 <= sink && sink < n);
assert(source != sink);
#ifndef NDEBUG
std::vector<int> incidence_count(graph.edge_count(), 0);
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
assert(0 <= edge.id && edge.id < graph.edge_count());
incidence_count[edge.id]++;
}
}
for (int edge_id = 0; edge_id < graph.edge_count(); edge_id++) {
if (graph.is_edge_alive(edge_id)) {
assert(incidence_count[edge_id] == 2);
}
}
#endif
std::vector<int> parent(n, -1);
std::vector<int> preorder(n, -1);
std::vector<int> low_vertex(n, -1);
std::vector<int> next_edge(n, 0);
std::vector<int> traversal;
traversal.reserve(n);
preorder[source] = 0;
low_vertex[source] = source;
traversal.push_back(source);
preorder[sink] = 1;
low_vertex[sink] = sink;
traversal.push_back(sink);
std::vector<int> stack(1, sink);
while (!stack.empty()) {
const int vertex = stack.back();
if (next_edge[vertex] < int(graph[vertex].size())) {
const Edge<T>& edge = graph[vertex][next_edge[vertex]++];
if (!edge.alive || edge.to == vertex) continue;
const int to = edge.to;
if (preorder[to] == -1) {
parent[to] = vertex;
preorder[to] = int(traversal.size());
low_vertex[to] = to;
traversal.push_back(to);
stack.push_back(to);
} else if (preorder[to] < preorder[low_vertex[vertex]]) {
low_vertex[vertex] = to;
}
continue;
}
stack.pop_back();
const int parent_vertex = parent[vertex];
if (parent_vertex != -1 &&
preorder[low_vertex[vertex]] <
preorder[low_vertex[parent_vertex]]) {
low_vertex[parent_vertex] = low_vertex[vertex];
}
}
if (int(traversal.size()) != n) return {};
std::vector<int> next(n, -1);
std::vector<int> previous(n, -1);
std::vector<int> sign(n, 0);
next[source] = sink;
previous[sink] = source;
sign[source] = -1;
for (int index = 2; index < n; index++) {
const int vertex = traversal[index];
const int parent_vertex = parent[vertex];
assert(parent_vertex != -1);
if (sign[low_vertex[vertex]] == -1) {
const int before = previous[parent_vertex];
if (before == -1) return {};
next[before] = vertex;
next[vertex] = parent_vertex;
previous[vertex] = before;
previous[parent_vertex] = vertex;
sign[parent_vertex] = 1;
} else {
const int after = next[parent_vertex];
if (after == -1) return {};
next[parent_vertex] = vertex;
next[vertex] = after;
previous[vertex] = parent_vertex;
previous[after] = vertex;
sign[parent_vertex] = -1;
}
}
std::vector<int> order;
order.reserve(n);
int vertex = source;
while (vertex != -1 && int(order.size()) <= n) {
order.push_back(vertex);
if (vertex == sink) break;
vertex = next[vertex];
}
if (int(order.size()) != n || order.back() != sink) return {};
std::vector<int> rank(n, -1);
for (int index = 0; index < n; index++) rank[order[index]] = index;
for (int index = 0; index < n; index++) {
const int current = order[index];
bool has_smaller = false;
bool has_larger = false;
for (const Edge<T>& edge : graph[current]) {
if (!edge.alive || edge.to == current) continue;
has_smaller = has_smaller || rank[edge.to] < index;
has_larger = has_larger || index < rank[edge.to];
}
if (index > 0 && !has_smaller) return {};
if (index + 1 < n && !has_larger) return {};
}
return rank;
}
} // namespace graph
} // namespace m1une
#line 1 "graph/three_edge_connected_components.hpp"
#line 9 "graph/three_edge_connected_components.hpp"
#line 11 "graph/three_edge_connected_components.hpp"
namespace m1une {
namespace graph {
struct ThreeEdgeConnectedComponentsResult {
std::vector<std::vector<int>> components;
std::vector<int> component_of_vertex;
int component_count() const {
return int(components.size());
}
bool same(int first, int second) const {
assert(0 <= first && first < int(component_of_vertex.size()));
assert(0 <= second && second < int(component_of_vertex.size()));
return component_of_vertex[first] == component_of_vertex[second];
}
};
namespace internal {
// Maintains every component as a circular linked list. Swapping two successors
// concatenates two different lists in O(1) time.
struct ThreeEdgeComponentCycles {
std::vector<int> next;
explicit ThreeEdgeComponentCycles(int n) : next(n) {
std::iota(next.begin(), next.end(), 0);
}
void unite(int first, int second) {
std::swap(next[first], next[second]);
}
ThreeEdgeConnectedComponentsResult build_result() const {
const int n = int(next.size());
ThreeEdgeConnectedComponentsResult result;
result.component_of_vertex.assign(n, -1);
for (int first = 0; first < n; first++) {
if (result.component_of_vertex[first] != -1) continue;
const int component = result.component_count();
result.components.emplace_back();
int vertex = first;
do {
result.component_of_vertex[vertex] = component;
result.components.back().push_back(vertex);
vertex = next[vertex];
} while (vertex != first);
}
return result;
}
};
} // namespace internal
// Decomposes an undirected multigraph into maximal vertex sets joined by at
// least three edge-disjoint paths. This is an iterative form of Tsin's
// one-pass contraction algorithm.
template <class T>
ThreeEdgeConnectedComponentsResult three_edge_connected_components(
const Graph<T>& graph
) {
const int n = graph.size();
const int edge_count = graph.edge_count();
#ifndef NDEBUG
std::vector<int> incidence_count(edge_count, 0);
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
assert(edge.from == vertex);
assert(0 <= edge.to && edge.to < n);
assert(0 <= edge.id && edge.id < edge_count);
incidence_count[edge.id]++;
}
}
for (int edge_id = 0; edge_id < edge_count; edge_id++) {
if (incidence_count[edge_id] != 0) assert(incidence_count[edge_id] == 2);
}
#endif
const int none = n;
std::vector<int> enter(n, -1);
std::vector<int> leave(n, 0);
std::vector<int> low(n, none);
std::vector<int> degree(n, 0);
std::vector<int> path(n, none);
std::vector<int> parent(n, -1);
std::vector<int> parent_edge(n, -1);
std::vector<int> next_edge(n, 0);
std::vector<int> dfs_stack;
internal::ThreeEdgeComponentCycles component_cycles(n);
int timer = 0;
auto absorb = [&](int vertex, int other) {
component_cycles.unite(vertex, other);
degree[vertex] += degree[other];
};
auto process_visited_edge = [&](int vertex, int to) {
if (enter[to] < enter[vertex]) {
degree[vertex]++;
low[vertex] = std::min(low[vertex], enter[to]);
return;
}
degree[vertex]--;
int current = path[vertex];
while (current != none && enter[current] <= enter[to] && enter[to] < leave[current]) {
absorb(vertex, current);
current = path[current];
}
path[vertex] = current;
};
auto process_child = [&](int vertex, int child) {
if (path[child] == none && degree[child] <= 1) {
degree[vertex] += degree[child];
low[vertex] = std::min(low[vertex], low[child]);
return;
}
int current = child;
if (degree[child] == 0) current = path[child];
assert(current != none);
if (low[current] < low[vertex]) {
low[vertex] = low[current];
std::swap(current, path[vertex]);
}
while (current != none) {
absorb(vertex, current);
current = path[current];
}
};
for (int root = 0; root < n; root++) {
if (enter[root] != -1) continue;
enter[root] = timer++;
dfs_stack.push_back(root);
while (!dfs_stack.empty()) {
const int vertex = dfs_stack.back();
if (next_edge[vertex] < int(graph[vertex].size())) {
const Edge<T>& edge = graph[vertex][next_edge[vertex]++];
if (!edge.alive || edge.from == edge.to || edge.id == parent_edge[vertex]) continue;
const int to = edge.to;
if (enter[to] == -1) {
parent[to] = vertex;
parent_edge[to] = edge.id;
enter[to] = timer++;
dfs_stack.push_back(to);
} else {
process_visited_edge(vertex, to);
}
continue;
}
leave[vertex] = timer;
dfs_stack.pop_back();
if (parent[vertex] != -1) process_child(parent[vertex], vertex);
}
}
return component_cycles.build_result();
}
} // namespace graph
} // namespace m1une
#line 1 "graph/two_edge_connected_components.hpp"
#line 6 "graph/two_edge_connected_components.hpp"
#line 8 "graph/two_edge_connected_components.hpp"
namespace m1une {
namespace graph {
struct TwoEdgeConnectedBridge {
int from;
int to;
int edge_id;
};
struct TwoEdgeConnectedComponentsResult {
std::vector<std::vector<int>> components;
std::vector<int> component_of_vertex;
std::vector<int> bridge_ids;
std::vector<char> bridge;
std::vector<TwoEdgeConnectedBridge> bridge_forest_edges;
std::vector<int> ord;
std::vector<int> low;
int component_count() const {
return int(components.size());
}
bool same(int first, int second) const {
assert(0 <= first && first < int(component_of_vertex.size()));
assert(0 <= second && second < int(component_of_vertex.size()));
return component_of_vertex[first] == component_of_vertex[second];
}
bool is_bridge(int edge_id) const {
assert(0 <= edge_id && edge_id < int(bridge.size()));
return bridge[edge_id];
}
};
// Removes every active bridge and returns the remaining connected components.
// The first lowlink traversal and the component traversal are both iterative.
template <class T>
TwoEdgeConnectedComponentsResult two_edge_connected_components(
const Graph<T>& graph
) {
const int n = graph.size();
const int edge_count = graph.edge_count();
TwoEdgeConnectedComponentsResult result;
result.component_of_vertex.assign(n, -1);
result.bridge.assign(edge_count, false);
result.ord.assign(n, -1);
result.low.assign(n, -1);
std::vector<int> edge_from(edge_count, -1);
std::vector<int> edge_to(edge_count, -1);
std::vector<int> incidence_count(edge_count, 0);
for (int vertex = 0; vertex < n; vertex++) {
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive) continue;
assert(0 <= edge.id && edge.id < edge_count);
if (incidence_count[edge.id] == 0) {
edge_from[edge.id] = edge.from;
edge_to[edge.id] = edge.to;
}
incidence_count[edge.id]++;
}
}
#ifndef NDEBUG
for (int edge_id = 0; edge_id < edge_count; edge_id++) {
if (incidence_count[edge_id] != 0) assert(incidence_count[edge_id] == 2);
}
#endif
std::vector<int> parent(n, -1);
std::vector<int> parent_edge(n, -1);
std::vector<int> next_edge(n, 0);
std::vector<int> stack;
int timer = 0;
for (int root = 0; root < n; root++) {
if (result.ord[root] != -1) continue;
result.ord[root] = result.low[root] = timer++;
stack.push_back(root);
while (!stack.empty()) {
const int vertex = stack.back();
if (next_edge[vertex] < int(graph[vertex].size())) {
const Edge<T>& edge = graph[vertex][next_edge[vertex]++];
if (!edge.alive || edge.id == parent_edge[vertex]) continue;
const int to = edge.to;
if (result.ord[to] == -1) {
parent[to] = vertex;
parent_edge[to] = edge.id;
result.ord[to] = result.low[to] = timer++;
stack.push_back(to);
} else if (result.ord[to] < result.low[vertex]) {
result.low[vertex] = result.ord[to];
}
continue;
}
stack.pop_back();
const int parent_vertex = parent[vertex];
if (parent_vertex == -1) continue;
if (result.low[vertex] < result.low[parent_vertex]) {
result.low[parent_vertex] = result.low[vertex];
}
if (result.ord[parent_vertex] < result.low[vertex]) {
result.bridge[parent_edge[vertex]] = true;
}
}
}
for (int root = 0; root < n; root++) {
if (result.component_of_vertex[root] != -1) continue;
const int component = result.component_count();
result.components.emplace_back();
result.component_of_vertex[root] = component;
stack.push_back(root);
while (!stack.empty()) {
const int vertex = stack.back();
stack.pop_back();
result.components.back().push_back(vertex);
for (const Edge<T>& edge : graph[vertex]) {
if (!edge.alive || result.bridge[edge.id]) continue;
if (result.component_of_vertex[edge.to] != -1) continue;
result.component_of_vertex[edge.to] = component;
stack.push_back(edge.to);
}
}
}
for (int edge_id = 0; edge_id < edge_count; edge_id++) {
if (!result.bridge[edge_id]) continue;
result.bridge_ids.push_back(edge_id);
const int first_component = result.component_of_vertex[edge_from[edge_id]];
const int second_component = result.component_of_vertex[edge_to[edge_id]];
assert(first_component != second_component);
result.bridge_forest_edges.push_back(
TwoEdgeConnectedBridge{first_component, second_component, edge_id});
}
return result;
}
} // namespace graph
} // namespace m1une
#line 32 "graph/undirected.hpp"
#line 15 "graph/all.hpp"