m1une's library

This documentation is automatically generated by online-judge-tools/verification-helper

View on GitHub

:heavy_check_mark: verify/graph/graph_algorithms.test.cpp

Depends on

Code

#define PROBLEM "https://judge.yosupo.jp/problem/aplusb"

#include <algorithm>
#include <cassert>
#include "../../utilities/fast_io.hpp"
#include <set>
#include <string>
#include <vector>

#include "../../graph/all.hpp"

using m1une::graph::Graph;

void test_graph_container() {
    Graph<int> g(2);
    assert(g.size() == 2);
    int added = g.add_vertex();
    assert(added == 2);
    int e0 = g.add_directed_edge(0, 1, 4);
    int e1 = g.add_edge(1, 2, 5);
    assert(e0 == 0);
    assert(e1 == 1);
    assert(g.edge_count() == 2);
    assert(g[1].size() == 1);
    assert(g.edges().size() == 2);
    auto rev = g.reversed();
    assert(rev[1][0].to == 0);
}

void test_edge_alive() {
    Graph<int> g(4);
    int e01 = g.add_edge(0, 1);
    int e12 = g.add_edge(1, 2);
    int e23 = g.add_edge(2, 3);
    (void)e01;
    (void)e23;

    assert(g.edge_count() == 3);
    assert(g.edges().size() == 3);
    auto res = m1une::graph::bfs(g, 0);
    assert(res.dist[3] == 3);

    g.erase_edge(e12);
    assert(!g.is_edge_alive(e12));
    assert(g.edges().size() == 2);
    assert(g.edges(true).size() == 3);
    auto cut = m1une::graph::bfs(g, 0);
    assert(!cut.reachable(3));

    auto rev = g.reversed();
    assert(!rev.is_edge_alive(e12));
    assert(rev.edges().size() == 2);

    g.revive_edge(e12);
    assert(g.is_edge_alive(e12));
    auto restored = m1une::graph::bfs(g, 0);
    assert(restored.dist[3] == 3);
}

void test_bfs() {
    Graph<int> g(5);
    g.add_directed_edge(0, 1);
    g.add_directed_edge(0, 2);
    g.add_directed_edge(1, 3);
    g.add_directed_edge(2, 3);
    g.add_directed_edge(3, 4);

    auto res = m1une::graph::bfs(g, 0);
    assert(res.dist[0] == 0);
    assert(res.dist[3] == 2);
    assert(res.dist[4] == 3);
    auto path = res.path(4);
    assert(path.front() == 0);
    assert(path.back() == 4);
    assert(path.size() == 4);
}

void test_dijkstra() {
    Graph<long long> g(5);
    g.add_directed_edge(0, 1, 4);
    g.add_directed_edge(0, 2, 1);
    g.add_directed_edge(2, 1, 2);
    g.add_directed_edge(1, 3, 1);
    g.add_directed_edge(2, 3, 7);
    g.add_directed_edge(3, 4, 3);

    auto res = m1une::graph::dijkstra(g, 0);
    assert(res.dist[1] == 3);
    assert(res.dist[4] == 7);
    assert((res.path(4) == std::vector<int>{0, 2, 1, 3, 4}));
}

void test_zero_one_bfs() {
    Graph<int> g(6);
    g.add_directed_edge(0, 1, 1);
    g.add_directed_edge(0, 2, 0);
    g.add_directed_edge(2, 1, 0);
    g.add_directed_edge(1, 3, 1);
    g.add_directed_edge(2, 3, 1);
    g.add_directed_edge(3, 4, 0);

    auto res = m1une::graph::zero_one_bfs(g, 0);
    assert(res.dist[0] == 0);
    assert(res.dist[1] == 0);
    assert(res.dist[3] == 1);
    assert(res.dist[4] == 1);
    assert(!res.reachable(5));
    assert((res.path(4) == std::vector<int>{0, 2, 3, 4}));

    auto multi = m1une::graph::zero_one_bfs(g, std::vector<int>{1, 5});
    assert(multi.dist[1] == 0);
    assert(multi.dist[4] == 1);
    assert(multi.dist[5] == 0);
}

void test_bellman_ford() {
    Graph<long long> g(5);
    g.add_directed_edge(0, 1, 1);
    g.add_directed_edge(1, 2, -3);
    g.add_directed_edge(2, 3, 1);
    g.add_directed_edge(3, 1, 1);
    g.add_directed_edge(0, 4, 5);

    auto res = m1une::graph::bellman_ford(g, 0);
    assert(res.has_negative_cycle);
    assert(res.affected_by_negative_cycle(1));
    assert(res.affected_by_negative_cycle(2));
    assert(res.affected_by_negative_cycle(3));
    assert(!res.affected_by_negative_cycle(4));
    assert(res.dist[4] == 5);
}

void test_dag_shortest_path() {
    Graph<long long> g(6);
    g.add_directed_edge(0, 1, 2);
    g.add_directed_edge(0, 2, 5);
    g.add_directed_edge(1, 2, -4);
    g.add_directed_edge(1, 4, 10);
    g.add_directed_edge(2, 3, 3);
    g.add_directed_edge(3, 4, 1);

    auto res = m1une::graph::dag_shortest_path(g, 0);
    assert(res.has_value());
    assert(res->dist[0] == 0);
    assert(res->dist[2] == -2);
    assert(res->dist[4] == 2);
    assert(!res->reachable(5));
    assert((res->path(4) == std::vector<int>{0, 1, 2, 3, 4}));
    assert(res->topological_order.size() == 6);

    auto multi = m1une::graph::dag_shortest_path(g, std::vector<int>{1, 5});
    assert(multi.has_value());
    assert(multi->dist[4] == 0);
    assert(multi->dist[5] == 0);

    g.add_directed_edge(4, 1, 1);
    auto cyclic = m1une::graph::dag_shortest_path(g, 0);
    assert(!cyclic.has_value());
}

void test_warshall_floyd() {
    Graph<long long> g(4);
    g.add_directed_edge(0, 1, 3);
    g.add_directed_edge(1, 2, 4);
    g.add_directed_edge(0, 2, 10);
    g.add_directed_edge(2, 3, -2);

    auto dist = m1une::graph::warshall_floyd(g);
    assert(dist[0][2] == 7);
    assert(dist[0][3] == 5);
    assert(!m1une::graph::has_negative_cycle(dist));

    bool changed = m1une::graph::warshall_floyd_add_directed_edge(dist, 3, 1, 1LL);
    assert(changed);
    assert(dist[0][1] == 3);
    assert(dist[2][1] == -1);
    assert(dist[3][2] == 5);

    changed = m1une::graph::warshall_floyd_add_directed_edge(dist, 0, 2, 100LL);
    assert(!changed);

    Graph<long long> undirected(4);
    undirected.add_edge(0, 1, 10);
    undirected.add_edge(1, 2, 10);
    undirected.add_edge(2, 3, 10);
    auto udist = m1une::graph::warshall_floyd(undirected);
    changed = m1une::graph::warshall_floyd_add_undirected_edge(udist, 0, 3, 1LL);
    assert(changed);
    assert(udist[0][3] == 1);
    assert(udist[3][0] == 1);
    assert(udist[1][3] == 11);
}

void test_topological_sort() {
    Graph<int> g(4);
    g.add_directed_edge(0, 1);
    g.add_directed_edge(0, 2);
    g.add_directed_edge(1, 3);
    g.add_directed_edge(2, 3);

    auto order = m1une::graph::topological_sort(g);
    assert(order.has_value());
    std::vector<int> pos(4);
    for (int i = 0; i < 4; i++) pos[(*order)[i]] = i;
    for (int v = 0; v < 4; v++) {
        for (const auto& e : g[v]) assert(pos[e.from] < pos[e.to]);
    }

    g.add_directed_edge(3, 0);
    assert(!m1une::graph::is_dag(g));
}

void test_scc() {
    Graph<int> g(4);
    g.add_directed_edge(0, 1);
    g.add_directed_edge(1, 0);
    g.add_directed_edge(1, 2);
    g.add_directed_edge(2, 3);
    g.add_directed_edge(3, 2);

    auto scc = m1une::graph::strongly_connected_components(g);
    assert(scc.count == 2);
    assert(scc.same(0, 1));
    assert(scc.same(2, 3));
    assert(!scc.same(0, 2));
    auto dag = scc.dag(g);
    assert(dag.size() == 2);
    assert(dag.edge_count() == 1);
}

void test_lowlink() {
    Graph<int> g(5);
    g.add_edge(0, 1);
    g.add_edge(1, 2);
    g.add_edge(2, 0);
    int b0 = g.add_edge(1, 3);
    int b1 = g.add_edge(3, 4);

    auto res = m1une::graph::lowlink(g);
    assert((res.articulation == std::vector<int>{1, 3}));
    assert((res.bridge_ids == std::vector<int>{b0, b1}));
}

void test_bipartite_and_components() {
    Graph<int> square(4);
    square.add_edge(0, 1);
    square.add_edge(1, 2);
    square.add_edge(2, 3);
    square.add_edge(3, 0);
    auto bp = m1une::graph::bipartite(square);
    assert(bp.is_bipartite);
    assert(bp.color[0] == bp.color[2]);
    assert((bp.left_vertices == std::vector<int>{0, 2}));
    assert((bp.right_vertices == std::vector<int>{1, 3}));
    assert(bp.left_id[2] == 1);
    assert(bp.right_id[3] == 1);
    auto built = m1une::graph::make_bipartite_matching(square);
    assert(built.has_value());
    assert(built->matching.left_size() == 2);
    assert(built->matching.right_size() == 2);
    assert(built->matching.max_matching() == 2);
    for (const auto& p : built->matching.matching()) {
        int u = built->left_vertex(p.left);
        int v = built->right_vertex(p.right);
        assert(bp.color[u] == 0);
        assert(bp.color[v] == 1);
        assert(square.is_edge_alive(built->original_edge(p.edge_id)));
    }

    Graph<int> triangle(3);
    triangle.add_edge(0, 1);
    triangle.add_edge(1, 2);
    triangle.add_edge(2, 0);
    assert(!m1une::graph::is_bipartite(triangle));
    assert(!m1une::graph::make_bipartite_matching(triangle).has_value());

    Graph<int> cc_graph(5);
    cc_graph.add_edge(0, 1);
    cc_graph.add_edge(2, 3);
    auto cc = m1une::graph::connected_components(cc_graph);
    assert(cc.count == 3);
    assert(cc.same(0, 1));
    assert(cc.same(2, 3));
    assert(!cc.same(0, 2));

    Graph<int> directed(2);
    directed.add_directed_edge(1, 0);
    assert(m1une::graph::is_bipartite(directed));
    auto weak = m1une::graph::connected_components(directed);
    assert(weak.count == 1);

    m1une::graph::BipartiteMatching bm(3, 2);
    int e00 = bm.add_edge(0, 0);
    int e10 = bm.add_edge(1, 0);
    int e11 = bm.add_edge(1, 1);
    int e21 = bm.add_edge(2, 1);
    assert(bm.left_size() == 3);
    assert(bm.right_size() == 2);
    assert(bm.edge_count() == 4);
    assert(bm.get_edge(e10).left == 1);
    assert(bm.max_matching() == 2);
    assert(bm.matching_size() == 2);
    auto pairs = bm.matching();
    assert(pairs.size() == 2);
    auto left_match = bm.left_match();
    auto right_match = bm.right_match();
    for (const auto& p : pairs) {
        assert(left_match[p.left] == p.right);
        assert(right_match[p.right] == p.left);
    }

    auto cover = bm.minimum_vertex_cover();
    assert(cover.left.empty());
    assert((cover.right == std::vector<int>{0, 1}));
    assert(cover.size() == 2);
    auto independent = bm.maximum_independent_set();
    assert((independent.left == std::vector<int>{0, 1, 2}));
    assert(independent.right.empty());

    auto edge_cover = bm.minimum_edge_cover();
    assert(edge_cover.has_value());
    assert(edge_cover->size() == 3);
    std::vector<bool> covered_left(3, false), covered_right(2, false);
    for (int id : *edge_cover) {
        auto edge = bm.get_edge(id);
        covered_left[edge.left] = true;
        covered_right[edge.right] = true;
    }
    assert((covered_left == std::vector<bool>{true, true, true}));
    assert((covered_right == std::vector<bool>{true, true}));

    bm.erase_edge(e11);
    bm.erase_edge(e21);
    assert(!bm.is_edge_alive(e21));
    assert(bm.edges().size() == 2);
    assert(bm.edges(true).size() == 4);
    assert(bm.max_matching() == 1);
    bm.revive_edge(e21);
    assert(bm.max_matching() == 2);

    m1une::graph::BipartiteMatching isolated(1, 1);
    assert(!isolated.minimum_edge_cover().has_value());

    (void)e00;
}

void test_general_matching() {
    m1une::graph::GeneralMatching blossom(6);
    int e01 = blossom.add_edge(0, 1);
    int e12 = blossom.add_edge(1, 2);
    int e23 = blossom.add_edge(2, 3);
    int e34 = blossom.add_edge(3, 4);
    int e40 = blossom.add_edge(4, 0);
    int e15 = blossom.add_edge(1, 5);
    (void)e12;
    (void)e23;
    (void)e34;
    (void)e40;

    assert(blossom.size() == 6);
    assert(blossom.edge_count() == 6);
    assert(blossom.get_edge(e01).other(0) == 1);
    assert(blossom.max_matching() == 3);
    assert(blossom.matching_size() == 3);
    auto mate = blossom.mate();
    auto mate_edge = blossom.mate_edge();
    for (int v = 0; v < 6; v++) {
        assert(mate[v] != -1);
        assert(mate[mate[v]] == v);
        assert(mate_edge[v] != -1);
    }
    auto pairs = blossom.matching();
    assert(pairs.size() == 3);
    for (const auto& p : pairs) {
        assert(mate[p.from] == p.to);
        assert(mate[p.to] == p.from);
    }

    auto edge_cover = blossom.minimum_edge_cover();
    assert(edge_cover.has_value());
    assert(edge_cover->size() == 3);
    std::vector<bool> covered(6, false);
    for (int id : *edge_cover) {
        auto edge = blossom.get_edge(id);
        covered[edge.from] = true;
        covered[edge.to] = true;
    }
    assert((covered == std::vector<bool>{true, true, true, true, true, true}));

    blossom.erase_edge(e15);
    assert(!blossom.is_edge_alive(e15));
    assert(blossom.edges().size() == 5);
    assert(blossom.edges(true).size() == 6);
    assert(blossom.max_matching() == 2);
    assert(!blossom.minimum_edge_cover().has_value());
    blossom.revive_edge(e15);
    assert(blossom.max_matching() == 3);

    m1une::graph::GeneralMatching tricky(6);
    tricky.add_edge(0, 1);
    tricky.add_edge(0, 4);
    tricky.add_edge(1, 2);
    tricky.add_edge(3, 4);
    tricky.add_edge(3, 5);
    tricky.add_edge(4, 5);
    assert(tricky.max_matching() == 3);
    for (const auto& p : tricky.matching()) {
        auto e = tricky.get_edge(p.edge_id);
        assert((e.from == p.from && e.to == p.to) || (e.from == p.to && e.to == p.from));
    }

    m1une::graph::GeneralMatching parallel(4);
    int p01_removed = parallel.add_edge(0, 1);
    parallel.add_edge(0, 1);
    parallel.add_edge(2, 3);
    parallel.erase_edge(p01_removed);
    assert(parallel.max_matching() == 2);
    auto parallel_mate_edge = parallel.mate_edge();
    for (int v = 0; v < 4; v++) assert(parallel.is_edge_alive(parallel_mate_edge[v]));

    m1une::graph::GeneralMatching path(3);
    path.add_edge(0, 1);
    path.add_edge(1, 2);
    auto path_cover = path.minimum_edge_cover();
    assert(path_cover.has_value());
    assert(path_cover->size() == 2);

    m1une::graph::GeneralMatching bipartite_general(8);
    int bg03 = bipartite_general.add_edge(0, 3);
    bipartite_general.add_edge(0, 7);
    bipartite_general.add_edge(1, 4);
    bipartite_general.add_edge(2, 5);
    bipartite_general.add_edge(6, 3);
    bipartite_general.add_edge(2, 7);
    bipartite_general.erase_edge(bg03);
    assert(bipartite_general.max_matching() == 4);
    auto bipartite_mate = bipartite_general.mate();
    for (int v = 0; v < 8; v++) {
        assert(bipartite_mate[v] != -1);
        assert(bipartite_mate[bipartite_mate[v]] == v);
    }

    m1une::graph::GeneralMatching isolated(1);
    assert(!isolated.minimum_edge_cover().has_value());

    Graph<int> g(4);
    int g01 = g.add_edge(0, 1);
    int g12 = g.add_edge(1, 2);
    int g20 = g.add_edge(2, 0);
    int g23 = g.add_edge(2, 3);
    (void)g01;
    (void)g12;
    (void)g20;
    auto built = m1une::graph::make_general_matching(g);
    assert(built.matching.max_matching() == 2);
    for (const auto& p : built.matching.matching()) {
        assert(g.is_edge_alive(built.original_edge(p.edge_id)));
    }
    assert(g.is_edge_alive(g23));
}

void test_maximum_clique_and_independent_set() {
    Graph<int> g(7);
    int removed_clique_edge = -1;
    for (int i = 0; i < 4; i++) {
        for (int j = i + 1; j < 4; j++) {
            int id = g.add_edge(i, j);
            if (i == 0 && j == 2) removed_clique_edge = id;
        }
    }
    g.add_edge(4, 0);
    g.add_edge(4, 1);
    g.add_edge(5, 1);
    g.add_edge(5, 2);

    auto clique = m1une::graph::maximum_clique(g);
    assert(clique.size() == 4);
    assert((clique.vertices == std::vector<int>{0, 1, 2, 3}));
    assert(m1une::graph::is_clique(g, clique.vertices));
    assert(m1une::graph::maximum_clique_size(g) == 4);

    auto independent = m1une::graph::maximum_independent_set(g);
    assert(independent.size() == 4);
    assert((independent.vertices == std::vector<int>{3, 4, 5, 6}));
    assert(m1une::graph::is_independent_set(g, independent.vertices));
    assert(m1une::graph::maximum_independent_set_size(g) == 4);

    auto cover = m1une::graph::minimum_vertex_cover(g);
    assert(cover.size() == 3);
    assert((cover.vertices == std::vector<int>{0, 1, 2}));
    assert(m1une::graph::is_vertex_cover(g, cover.vertices));
    assert(m1une::graph::minimum_vertex_cover_size(g) == 3);

    assert(!m1une::graph::is_clique(g, std::vector<int>{0, 2, 4}));
    assert(!m1une::graph::is_independent_set(g, std::vector<int>{4, 0}));
    assert(!m1une::graph::is_vertex_cover(g, std::vector<int>{0, 1}));

    g.erase_edge(removed_clique_edge);
    assert(m1une::graph::maximum_clique_size(g) == 3);
    g.revive_edge(removed_clique_edge);
    assert(m1une::graph::maximum_clique_size(g) == 4);

    Graph<int> directed(3);
    directed.add_directed_edge(0, 1);
    directed.add_directed_edge(1, 2);
    auto directed_clique = m1une::graph::maximum_clique(directed);
    auto directed_independent = m1une::graph::maximum_independent_set(directed);
    auto directed_cover = m1une::graph::minimum_vertex_cover(directed);
    assert(directed_clique.size() == 2);
    assert(directed_independent.size() == 2);
    assert((directed_independent.vertices == std::vector<int>{0, 2}));
    assert((directed_cover.vertices == std::vector<int>{1}));

    Graph<int> empty(4);
    assert(m1une::graph::maximum_clique_size(empty) == 1);
    assert(m1une::graph::maximum_independent_set_size(empty) == 4);
    assert(m1une::graph::minimum_vertex_cover(empty).empty());

    Graph<int> path(6);
    for (int i = 0; i + 1 < 6; i++) path.add_edge(i, i + 1);
    assert(m1une::graph::maximum_independent_set_size(path) == 3);
    assert(m1une::graph::minimum_vertex_cover_size(path) == 3);

    Graph<int> cycle(5);
    for (int i = 0; i < 5; i++) cycle.add_edge(i, (i + 1) % 5);
    assert(m1une::graph::maximum_independent_set_size(cycle) == 2);
    assert(m1une::graph::minimum_vertex_cover_size(cycle) == 3);

    Graph<int> none(0);
    assert(m1une::graph::maximum_clique(none).empty());
    assert(m1une::graph::maximum_independent_set(none).empty());
    assert(m1une::graph::minimum_vertex_cover(none).empty());
}

void test_cycle_detection() {
    Graph<int> dg(3);
    dg.add_directed_edge(0, 1);
    dg.add_directed_edge(1, 2);
    dg.add_directed_edge(2, 0);
    auto directed = m1une::graph::find_directed_cycle(dg);
    assert(!directed.empty());
    assert(directed.vertices.front() == directed.vertices.back());
    assert(directed.edge_ids.size() + 1 == directed.vertices.size());

    Graph<int> ug(4);
    ug.add_edge(0, 1);
    ug.add_edge(1, 2);
    ug.add_edge(2, 0);
    ug.add_edge(2, 3);
    auto undirected = m1une::graph::find_undirected_cycle(ug);
    assert(!undirected.empty());
    assert(undirected.vertices.front() == undirected.vertices.back());
}

void test_kruskal() {
    Graph<long long> g(4);
    g.add_edge(0, 1, 1);
    g.add_edge(1, 2, 2);
    g.add_edge(2, 3, 3);
    g.add_edge(0, 3, 10);
    g.add_edge(0, 2, 4);

    auto mst = m1une::graph::kruskal(g);
    assert(mst.cost == 6);
    assert(mst.edges.size() == 3);
    assert(mst.components == 1);
    assert(mst.is_spanning_tree(g.size()));
}

void test_grid() {
    m1une::graph::Grid grid(3, 4);
    assert(grid.height() == 3);
    assert(grid.width() == 4);
    assert(grid.size() == 12);
    assert(grid.inside(2, 3));
    assert(!grid.inside(3, 0));
    assert(grid.id(2, 3) == 11);
    assert(grid.pos(6) == std::make_pair(1, 2));

    auto adj4 = grid.adj4(0, 0);
    std::vector<std::pair<int, int>> expected_adj4 = {
        std::pair<int, int>{0, 1},
        std::pair<int, int>{1, 0},
    };
    assert(adj4 == expected_adj4);

    auto adj8 = grid.adj8(1, 1);
    assert(adj8.size() == 8);
    auto adj4_ids = grid.adj4_ids(grid.id(1, 1));
    std::set<int> expected_ids = {grid.id(0, 1), grid.id(1, 2), grid.id(2, 1), grid.id(1, 0)};
    assert(std::set<int>(adj4_ids.begin(), adj4_ids.end()) == expected_ids);

    std::vector<std::string> s = {
        "....",
        ".##.",
        "....",
    };
    auto passable = [&](int i, int j) {
        return s[i][j] != '#';
    };

    auto g4 = grid.graph4(passable);
    assert(g4.size() == grid.size());
    assert(g4[grid.id(1, 1)].empty());
    auto res = m1une::graph::bfs(g4, grid.id(0, 0));
    assert(res.dist[grid.id(2, 3)] == 5);
    assert(res.dist[grid.id(1, 1)] == -1);

    auto g8 = grid.graph8(passable);
    auto res8 = m1une::graph::bfs(g8, grid.id(0, 0));
    assert(res8.dist[grid.id(2, 3)] == 4);

    auto all4 = grid.graph4();
    assert(all4.edge_count() == 17);
}

int main() {
    m1une::utilities::FastInput fast_input;
    m1une::utilities::FastOutput fast_output;

    test_graph_container();
    test_edge_alive();
    test_bfs();
    test_dijkstra();
    test_zero_one_bfs();
    test_bellman_ford();
    test_dag_shortest_path();
    test_warshall_floyd();
    test_topological_sort();
    test_scc();
    test_lowlink();
    test_bipartite_and_components();
    test_general_matching();
    test_maximum_clique_and_independent_set();
    test_cycle_detection();
    test_kruskal();
    test_grid();
    long long a, b;
    fast_input >> a >> b;
    fast_output << a + b << '\n';
}
#line 1 "verify/graph/graph_algorithms.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/aplusb"

#include <algorithm>
#include <cassert>
#line 1 "utilities/fast_io.hpp"



#line 5 "utilities/fast_io.hpp"
#include <array>
#include <cerrno>
#include <charconv>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <string>
#include <sys/stat.h>
#include <type_traits>
#include <utility>
#include <unistd.h>
#include <vector>

namespace m1une {
namespace utilities {

struct FastOutput;

namespace internal {

// Shared with the convenience helpers in template.hpp.
inline FastOutput* standard_output_instance = nullptr;

// Detect std::begin(x), std::end(x).
template <class T, class = void>
struct is_range : std::false_type {};

template <class T>
struct is_range<T, std::void_t<
    decltype(std::begin(std::declval<T&>())),
    decltype(std::end(std::declval<T&>()))
>> : std::true_type {};

template <class T>
inline constexpr bool is_range_v = is_range<T>::value;

template <class T>
using range_reference_t = decltype(*std::begin(std::declval<T&>()));

template <class T>
using range_value_t = std::remove_cv_t<std::remove_reference_t<range_reference_t<T>>>;

template <class T, class = void>
struct range_stored_value {
    using type = range_value_t<T>;
};

template <class T>
struct range_stored_value<T, std::void_t<typename std::remove_cv_t<std::remove_reference_t<T>>::value_type>> {
    using type = typename std::remove_cv_t<std::remove_reference_t<T>>::value_type;
};

template <class T>
using range_stored_value_t = typename range_stored_value<T>::type;

// Treat strings and C strings as scalar output objects, not as ranges.
template <class T>
struct is_char_array : std::false_type {};

template <class T, std::size_t N>
struct is_char_array<T[N]>
    : std::bool_constant<std::is_same_v<std::remove_cv_t<T>, char>> {};

template <class T>
struct is_string_like
    : std::bool_constant<
          std::is_same_v<std::decay_t<T>, std::string>
          || std::is_same_v<std::decay_t<T>, const char*>
          || std::is_same_v<std::decay_t<T>, char*>
          || is_char_array<std::remove_reference_t<T>>::value
      > {};

template <class T>
inline constexpr bool is_string_like_v = is_string_like<T>::value;

// ModInt-like type: x.val() is printable, and x can be assigned from long long.
template <class T, class = void>
struct has_val_method : std::false_type {};

template <class T>
struct has_val_method<T, std::void_t<decltype(std::declval<const T&>().val())>>
    : std::true_type {};

template <class T>
inline constexpr bool has_val_method_v = has_val_method<T>::value;

template <class T, class = void>
struct has_static_mod_raw : std::false_type {};

template <class T>
struct has_static_mod_raw<
    T, std::void_t<decltype(T::mod()), decltype(T::raw(std::declval<uint32_t>()))>>
    : std::true_type {};

template <class T>
inline constexpr bool has_static_mod_raw_v = has_static_mod_raw<T>::value;

// libstdc++ before GCC 16 does not classify __int128 as an integral type in
// strict ISO modes such as -std=c++23. Keep the fast-I/O interface independent
// of that implementation detail.
template <class T>
inline constexpr bool is_integral_v =
    std::is_integral_v<T>
    || std::is_same_v<std::remove_cv_t<T>, __int128_t>
    || std::is_same_v<std::remove_cv_t<T>, __uint128_t>;

template <class T>
inline constexpr bool is_signed_v =
    std::is_signed_v<T>
    || std::is_same_v<std::remove_cv_t<T>, __int128_t>;

template <class T>
struct make_unsigned {
    using type = std::make_unsigned_t<T>;
};

template <>
struct make_unsigned<__int128_t> {
    using type = __uint128_t;
};

template <>
struct make_unsigned<__uint128_t> {
    using type = __uint128_t;
};

template <class T>
using make_unsigned_t = typename make_unsigned<std::remove_cv_t<T>>::type;

}  // namespace internal

struct FastInput {
    static constexpr int buffer_size = 1 << 20;

   private:
    std::FILE* _stream;
    char _buffer[buffer_size];
    int _position;
    int _length;
    int _file_descriptor;
    bool _streaming;

    bool refill() {
        _position = 0;
        if (_streaming) {
            ssize_t length;
            do {
                length = ::read(_file_descriptor, _buffer, buffer_size);
            } while (length < 0 && errno == EINTR);
            if (length <= 0) {
                _length = 0;
                return false;
            }
            _length = int(length);
        } else {
            _length = int(std::fread(_buffer, 1, buffer_size, _stream));
        }
        return _length != 0;
    }

    template <class T>
    bool read_integer_from_stream(T& value) {
        if (!skip_spaces()) return false;
        int c = read_char_raw();

        bool negative = false;
        if (c == '-') {
            negative = true;
            c = read_char_raw();
        }

        if constexpr (internal::is_signed_v<T>) {
            T result = 0;
            while ('0' <= c && c <= '9') {
                result = negative ? result * 10 - (c - '0')
                                  : result * 10 + (c - '0');
                c = read_char_raw();
            }
            value = result;
        } else {
            T result = 0;
            while ('0' <= c && c <= '9') {
                result = result * 10 + T(c - '0');
                c = read_char_raw();
            }
            value = negative ? T(0) - result : result;
        }
        return true;
    }

    bool prepare_number() {
        if (_length - _position >= 64) return true;
        const int remaining = _length - _position;
        if (remaining > 0) std::memmove(_buffer, _buffer + _position, remaining);
        const int added = int(std::fread(_buffer + remaining, 1, buffer_size - remaining, _stream));
        _position = 0;
        _length = remaining + added;
        if (_length < buffer_size) _buffer[_length] = '\0';
        return _length != 0;
    }

   public:
    explicit FastInput(std::FILE* stream = stdin)
        : _stream(stream),
          _position(0),
          _length(0),
          _file_descriptor(::fileno(stream)),
          _streaming([&] {
              struct stat status;
              return _file_descriptor >= 0
                     && ::fstat(_file_descriptor, &status) == 0
                     && !S_ISREG(status.st_mode);
          }()) {}

    FastInput(const FastInput&) = delete;
    FastInput& operator=(const FastInput&) = delete;

    int read_char_raw() {
        if (_position == _length && !refill()) return EOF;
        return _buffer[_position++];
    }

    bool skip_spaces() {
        int c = read_char_raw();
        while (c != EOF && c <= ' ') c = read_char_raw();
        if (c == EOF) return false;
        --_position;
        return true;
    }

    bool read(char& value) {
        if (!skip_spaces()) return false;
        value = char(read_char_raw());
        return true;
    }

    bool read(std::string& value) {
        if (!skip_spaces()) return false;
        value.clear();
        while (true) {
            const int begin = _position;
            while (_position < _length &&
                   static_cast<unsigned char>(_buffer[_position]) > ' ') {
                ++_position;
            }
            value.append(_buffer + begin, _position - begin);
            if (_position < _length) {
                ++_position;
                return true;
            }
            if (!refill()) return true;
        }
    }

    bool read(bool& value) {
        int x;
        if (!read(x)) return false;
        value = x != 0;
        return true;
    }

    template <class T>
    std::enable_if_t<
        internal::is_integral_v<T>
            && !std::is_same_v<std::remove_cv_t<T>, bool>
            && !std::is_same_v<std::remove_cv_t<T>, char>,
        bool
    >
    read(T& value) {
        if (_streaming) return read_integer_from_stream(value);
        if (!prepare_number()) return false;
        int c = static_cast<unsigned char>(_buffer[_position++]);
        while (c <= ' ') c = static_cast<unsigned char>(_buffer[_position++]);

        bool negative = false;
        if (c == '-') {
            negative = true;
            c = static_cast<unsigned char>(_buffer[_position++]);
        }

        if constexpr (internal::is_signed_v<T>) {
            T result = 0;
            while ('0' <= c && c <= '9') {
                const int first = c - '0';
                const int second = static_cast<unsigned char>(_buffer[_position]) - '0';
                if (0 <= second && second <= 9) {
                    result = negative ? result * 100 - (first * 10 + second)
                                      : result * 100 + (first * 10 + second);
                    ++_position;
                } else {
                    result = negative ? result * 10 - first : result * 10 + first;
                }
                c = static_cast<unsigned char>(_buffer[_position++]);
            }
            value = result;
        } else {
            T result = 0;
            while ('0' <= c && c <= '9') {
                const unsigned first = unsigned(c - '0');
                const int second = static_cast<unsigned char>(_buffer[_position]) - '0';
                if (0 <= second && second <= 9) {
                    result = result * 100 + T(first * 10 + unsigned(second));
                    ++_position;
                } else {
                    result = result * 10 + T(first);
                }
                c = static_cast<unsigned char>(_buffer[_position++]);
            }
            value = negative ? T(0) - result : result;
        }
        if (_position > _length) _position = _length;
        return true;
    }

    template <class T>
    std::enable_if_t<std::is_floating_point_v<T>, bool>
    read(T& value) {
        if (!skip_spaces()) return false;
        int c = read_char_raw();
        bool negative = false;
        if (c == '-' || c == '+') {
            negative = c == '-';
            c = read_char_raw();
        }

        long double result = 0;
        while ('0' <= c && c <= '9') {
            result = result * 10 + (c - '0');
            c = read_char_raw();
        }
        if (c == '.') {
            long double place = 0.1L;
            c = read_char_raw();
            while ('0' <= c && c <= '9') {
                result += (c - '0') * place;
                place *= 0.1L;
                c = read_char_raw();
            }
        }
        if (c == 'e' || c == 'E') {
            c = read_char_raw();
            bool exponent_negative = false;
            if (c == '-' || c == '+') {
                exponent_negative = c == '-';
                c = read_char_raw();
            }
            int exponent = 0;
            while ('0' <= c && c <= '9') {
                exponent = exponent * 10 + (c - '0');
                c = read_char_raw();
            }
            long double scale = 1;
            long double power = 10;
            while (exponent > 0) {
                if (exponent & 1) scale *= power;
                power *= power;
                exponent >>= 1;
            }
            result = exponent_negative ? result / scale : result * scale;
        }
        value = static_cast<T>(negative ? -result : result);
        return true;
    }

    template <class T>
    std::enable_if_t<
        internal::has_val_method_v<T>
            && !internal::is_integral_v<T>
            && !internal::is_range_v<T>,
        bool
    >
    read(T& value) {
        long long x;
        if (!read(x)) return false;
        if constexpr (internal::has_static_mod_raw_v<T>) {
            if (x >= 0 && uint64_t(x) < uint64_t(T::mod())) {
                value = T::raw(uint32_t(x));
            } else {
                value = T(x);
            }
        } else {
            value = T(x);
        }
        return true;
    }

    template <class First, class Second>
    bool read(std::pair<First, Second>& value) {
        if (!read(value.first)) return false;
        return read(value.second);
    }

    template <class Range>
    std::enable_if_t<
        internal::is_range_v<Range>
            && !internal::is_string_like_v<Range>,
        bool
    >
    read(Range& range) {
        using StoredValue = internal::range_stored_value_t<Range>;
        constexpr bool nested = internal::is_range_v<StoredValue>
                                && !internal::is_string_like_v<StoredValue>;

        for (auto&& value : range) {
            if constexpr (std::is_same_v<StoredValue, bool> && !nested) {
                bool x;
                if (!read(x)) return false;
                value = x;
            } else {
                if (!read(value)) return false;
            }
        }
        return true;
    }

    template <class First, class Second, class... Rest>
    bool read(First& first, Second& second, Rest&... rest) {
        if (!read(first)) return false;
        return read(second, rest...);
    }

    template <class T>
    FastInput& operator>>(T& value) {
        if (!read(value)) std::abort();
        return *this;
    }
};

struct FastOutput {
    static constexpr int buffer_size = 1 << 20;

   private:
    inline static const auto digit_quads = [] {
        std::array<char, 40000> result{};
        for (int i = 0; i < 10000; i++) {
            int value = i;
            for (int j = 3; j >= 0; j--) {
                result[4 * i + j] = char('0' + value % 10);
                value /= 10;
            }
        }
        return result;
    }();

    std::FILE* _stream;
    char _buffer[buffer_size];
    int _position;
    int _precision;
    std::chars_format _float_format;
    char _range_separator;
    std::string* _capture = nullptr;

    template <class T>
    std::string format_cell(const T& value) {
        std::string result;
        struct CaptureGuard {
            std::string*& target;
            std::string* previous;
            ~CaptureGuard() { target = previous; }
        } guard{_capture, _capture};
        _capture = &result;
        write(value);
        return result;
    }

    template <class Matrix>
    void write_aligned_matrix(const Matrix& matrix) {
        std::vector<std::vector<std::string>> rows;
        std::vector<std::size_t> widths;
        for (const auto& row : matrix) {
            auto& cells = rows.emplace_back();
            std::size_t column = 0;
            for (const auto& value : row) {
                cells.push_back(format_cell(value));
                if (column == widths.size()) widths.push_back(0);
                widths[column] = std::max(widths[column], cells.back().size());
                ++column;
            }
        }
        bool first = true;
        for (const auto& row : rows) {
            if (!first) write_char('\n');
            first = false;
            for (std::size_t column = 0; column < row.size(); ++column) {
                if (column != 0) write_char(_range_separator);
                for (std::size_t padding = row[column].size();
                     padding < widths[column]; ++padding) {
                    write_char(' ');
                }
                write(row[column]);
            }
        }
    }

   public:
    explicit FastOutput(std::FILE* stream = stdout)
        : _stream(stream),
          _position(0),
          _precision(6),
          _float_format(std::chars_format::general),
          _range_separator(' ') {
        if (_stream == stdout
            && internal::standard_output_instance == nullptr) {
            internal::standard_output_instance = this;
        }
    }

    FastOutput(const FastOutput&) = delete;
    FastOutput& operator=(const FastOutput&) = delete;

    ~FastOutput() {
        flush();
        if (internal::standard_output_instance == this) {
            internal::standard_output_instance = nullptr;
        }
    }

    void flush() {
        if (_position != 0) {
            std::fwrite(_buffer, 1, _position, _stream);
            _position = 0;
        }
        std::fflush(_stream);
    }

    void write_char(char c) {
        if (_capture != nullptr) {
            _capture->push_back(c);
            return;
        }
        if (_position == buffer_size) flush();
        _buffer[_position++] = c;
    }

    void write(const char* s) {
        while (*s != '\0') write_char(*s++);
    }

    void write(const std::string& s) {
        if (_capture != nullptr) {
            _capture->append(s);
            return;
        }
        std::size_t position = 0;
        while (position < s.size()) {
            if (_position == buffer_size) flush();
            const std::size_t copied =
                std::min<std::size_t>(buffer_size - _position, s.size() - position);
            std::memcpy(_buffer + _position, s.data() + position, copied);
            _position += int(copied);
            position += copied;
        }
    }

    void write(char c) {
        write_char(c);
    }

    void write(bool value) {
        write_char(value ? '1' : '0');
    }

    template <class T>
    std::enable_if_t<std::is_floating_point_v<T>>
    write(T value) {
        char digits[128];
        auto [end, error] = std::to_chars(
            digits,
            digits + sizeof(digits),
            value,
            _float_format,
            _precision
        );
        if (error != std::errc()) std::abort();
        for (const char* pointer = digits; pointer != end; pointer++) {
            write_char(*pointer);
        }
    }

    template <class T>
    std::enable_if_t<
        internal::is_integral_v<T>
            && !std::is_same_v<std::remove_cv_t<T>, bool>
            && !std::is_same_v<std::remove_cv_t<T>, char>
    >
    write(T value) {
        using Raw = std::remove_cv_t<T>;
        using Unsigned = internal::make_unsigned_t<Raw>;

        Unsigned magnitude;
        if constexpr (internal::is_signed_v<Raw>) {
            if (value < 0) {
                write_char('-');
                magnitude = Unsigned(0) - Unsigned(value);
            } else {
                magnitude = Unsigned(value);
            }
        } else {
            magnitude = value;
        }

        if (magnitude == 0) {
            write_char('0');
            return;
        }

        unsigned chunks[16];
        int count = 0;
        while (magnitude >= 10000) {
            const Unsigned quotient = magnitude / 10000;
            chunks[count++] = unsigned(magnitude - quotient * 10000);
            magnitude = quotient;
        }
        if (_capture == nullptr && _position > buffer_size - 64) flush();
        char captured[64];
        char* const begin = _capture != nullptr ? captured : _buffer + _position;
        char* destination = begin;
        const unsigned leading = unsigned(magnitude);
        const char* first = digit_quads.data() + 4 * leading;
        int skip = leading < 10 ? 3 : leading < 100 ? 2 : leading < 1000 ? 1 : 0;
        for (; skip < 4; skip++) *destination++ = first[skip];
        while (count--) {
            const char* digits = digit_quads.data() + 4 * chunks[count];
            std::memcpy(destination, digits, 4);
            destination += 4;
        }
        if (_capture != nullptr) {
            _capture->append(begin, destination - begin);
        } else {
            _position += int(destination - begin);
        }
    }

    template <class T>
    std::enable_if_t<
        internal::has_val_method_v<T>
            && !internal::is_integral_v<T>
            && !internal::is_range_v<T>
    >
    write(const T& value) {
        write(value.val());
    }

    template <class First, class Second>
    void write(const std::pair<First, Second>& value) {
        write(value.first);
        write_char(' ');
        write(value.second);
    }

    template <class Range>
    std::enable_if_t<
        internal::is_range_v<Range>
            && !internal::is_string_like_v<Range>
    >
    write(const Range& range) {
        using StoredValue = internal::range_stored_value_t<const Range>;
        constexpr bool nested = internal::is_range_v<StoredValue>
                                && !internal::is_string_like_v<StoredValue>;

        bool first = true;
        for (const auto& value : range) {
            if (!first) write_char(nested ? '\n' : _range_separator);
            first = false;
            if constexpr (std::is_same_v<StoredValue, bool> && !nested) {
                write(static_cast<bool>(value));
            } else {
                write(value);
            }
        }
    }

    template <class First, class... Rest>
    void print(const First& first, const Rest&... rest) {
        write(first);
        ((write_char(' '), write(rest)), ...);
    }

    void println() {
        write_char('\n');
    }

    void set_precision(int precision) {
        _precision = precision;
    }

    void set_fixed(int precision = 6) {
        _float_format = std::chars_format::fixed;
        _precision = precision;
    }

    void set_general(int precision = 6) {
        _float_format = std::chars_format::general;
        _precision = precision;
    }

    void set_range_separator(char separator) {
        _range_separator = separator;
    }

    template <class Matrix>
    void write_aligned(const Matrix& matrix) {
        using Row = internal::range_stored_value_t<const Matrix>;
        using Cell = internal::range_stored_value_t<const Row>;
        static_assert(internal::is_range_v<Row> && !internal::is_string_like_v<Row>,
                      "write_aligned requires a two-dimensional range");
        static_assert(!internal::is_range_v<Cell> || internal::is_string_like_v<Cell>,
                      "write_aligned requires scalar cells");
        write_aligned_matrix(matrix);
    }

    template <class Matrix>
    void println_aligned(const Matrix& matrix) {
        write_aligned(matrix);
        write_char('\n');
    }

    template <class... Args>
    void println(const Args&... args) {
        print(args...);
        write_char('\n');
    }

    template <class T>
    FastOutput& operator<<(const T& value) {
        write(value);
        return *this;
    }
};

}  // namespace utilities
}  // namespace m1une


#line 6 "verify/graph/graph_algorithms.test.cpp"
#include <set>
#line 9 "verify/graph/graph_algorithms.test.cpp"

#line 1 "graph/all.hpp"



#line 1 "graph/counting.hpp"



#line 6 "graph/counting.hpp"
#include <optional>
#line 9 "graph/counting.hpp"

#line 1 "math/fps/convolution.hpp"



#line 9 "math/fps/convolution.hpp"
#include <new>
#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 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 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"


#line 11 "verify/graph/graph_algorithms.test.cpp"

using m1une::graph::Graph;

void test_graph_container() {
    Graph<int> g(2);
    assert(g.size() == 2);
    int added = g.add_vertex();
    assert(added == 2);
    int e0 = g.add_directed_edge(0, 1, 4);
    int e1 = g.add_edge(1, 2, 5);
    assert(e0 == 0);
    assert(e1 == 1);
    assert(g.edge_count() == 2);
    assert(g[1].size() == 1);
    assert(g.edges().size() == 2);
    auto rev = g.reversed();
    assert(rev[1][0].to == 0);
}

void test_edge_alive() {
    Graph<int> g(4);
    int e01 = g.add_edge(0, 1);
    int e12 = g.add_edge(1, 2);
    int e23 = g.add_edge(2, 3);
    (void)e01;
    (void)e23;

    assert(g.edge_count() == 3);
    assert(g.edges().size() == 3);
    auto res = m1une::graph::bfs(g, 0);
    assert(res.dist[3] == 3);

    g.erase_edge(e12);
    assert(!g.is_edge_alive(e12));
    assert(g.edges().size() == 2);
    assert(g.edges(true).size() == 3);
    auto cut = m1une::graph::bfs(g, 0);
    assert(!cut.reachable(3));

    auto rev = g.reversed();
    assert(!rev.is_edge_alive(e12));
    assert(rev.edges().size() == 2);

    g.revive_edge(e12);
    assert(g.is_edge_alive(e12));
    auto restored = m1une::graph::bfs(g, 0);
    assert(restored.dist[3] == 3);
}

void test_bfs() {
    Graph<int> g(5);
    g.add_directed_edge(0, 1);
    g.add_directed_edge(0, 2);
    g.add_directed_edge(1, 3);
    g.add_directed_edge(2, 3);
    g.add_directed_edge(3, 4);

    auto res = m1une::graph::bfs(g, 0);
    assert(res.dist[0] == 0);
    assert(res.dist[3] == 2);
    assert(res.dist[4] == 3);
    auto path = res.path(4);
    assert(path.front() == 0);
    assert(path.back() == 4);
    assert(path.size() == 4);
}

void test_dijkstra() {
    Graph<long long> g(5);
    g.add_directed_edge(0, 1, 4);
    g.add_directed_edge(0, 2, 1);
    g.add_directed_edge(2, 1, 2);
    g.add_directed_edge(1, 3, 1);
    g.add_directed_edge(2, 3, 7);
    g.add_directed_edge(3, 4, 3);

    auto res = m1une::graph::dijkstra(g, 0);
    assert(res.dist[1] == 3);
    assert(res.dist[4] == 7);
    assert((res.path(4) == std::vector<int>{0, 2, 1, 3, 4}));
}

void test_zero_one_bfs() {
    Graph<int> g(6);
    g.add_directed_edge(0, 1, 1);
    g.add_directed_edge(0, 2, 0);
    g.add_directed_edge(2, 1, 0);
    g.add_directed_edge(1, 3, 1);
    g.add_directed_edge(2, 3, 1);
    g.add_directed_edge(3, 4, 0);

    auto res = m1une::graph::zero_one_bfs(g, 0);
    assert(res.dist[0] == 0);
    assert(res.dist[1] == 0);
    assert(res.dist[3] == 1);
    assert(res.dist[4] == 1);
    assert(!res.reachable(5));
    assert((res.path(4) == std::vector<int>{0, 2, 3, 4}));

    auto multi = m1une::graph::zero_one_bfs(g, std::vector<int>{1, 5});
    assert(multi.dist[1] == 0);
    assert(multi.dist[4] == 1);
    assert(multi.dist[5] == 0);
}

void test_bellman_ford() {
    Graph<long long> g(5);
    g.add_directed_edge(0, 1, 1);
    g.add_directed_edge(1, 2, -3);
    g.add_directed_edge(2, 3, 1);
    g.add_directed_edge(3, 1, 1);
    g.add_directed_edge(0, 4, 5);

    auto res = m1une::graph::bellman_ford(g, 0);
    assert(res.has_negative_cycle);
    assert(res.affected_by_negative_cycle(1));
    assert(res.affected_by_negative_cycle(2));
    assert(res.affected_by_negative_cycle(3));
    assert(!res.affected_by_negative_cycle(4));
    assert(res.dist[4] == 5);
}

void test_dag_shortest_path() {
    Graph<long long> g(6);
    g.add_directed_edge(0, 1, 2);
    g.add_directed_edge(0, 2, 5);
    g.add_directed_edge(1, 2, -4);
    g.add_directed_edge(1, 4, 10);
    g.add_directed_edge(2, 3, 3);
    g.add_directed_edge(3, 4, 1);

    auto res = m1une::graph::dag_shortest_path(g, 0);
    assert(res.has_value());
    assert(res->dist[0] == 0);
    assert(res->dist[2] == -2);
    assert(res->dist[4] == 2);
    assert(!res->reachable(5));
    assert((res->path(4) == std::vector<int>{0, 1, 2, 3, 4}));
    assert(res->topological_order.size() == 6);

    auto multi = m1une::graph::dag_shortest_path(g, std::vector<int>{1, 5});
    assert(multi.has_value());
    assert(multi->dist[4] == 0);
    assert(multi->dist[5] == 0);

    g.add_directed_edge(4, 1, 1);
    auto cyclic = m1une::graph::dag_shortest_path(g, 0);
    assert(!cyclic.has_value());
}

void test_warshall_floyd() {
    Graph<long long> g(4);
    g.add_directed_edge(0, 1, 3);
    g.add_directed_edge(1, 2, 4);
    g.add_directed_edge(0, 2, 10);
    g.add_directed_edge(2, 3, -2);

    auto dist = m1une::graph::warshall_floyd(g);
    assert(dist[0][2] == 7);
    assert(dist[0][3] == 5);
    assert(!m1une::graph::has_negative_cycle(dist));

    bool changed = m1une::graph::warshall_floyd_add_directed_edge(dist, 3, 1, 1LL);
    assert(changed);
    assert(dist[0][1] == 3);
    assert(dist[2][1] == -1);
    assert(dist[3][2] == 5);

    changed = m1une::graph::warshall_floyd_add_directed_edge(dist, 0, 2, 100LL);
    assert(!changed);

    Graph<long long> undirected(4);
    undirected.add_edge(0, 1, 10);
    undirected.add_edge(1, 2, 10);
    undirected.add_edge(2, 3, 10);
    auto udist = m1une::graph::warshall_floyd(undirected);
    changed = m1une::graph::warshall_floyd_add_undirected_edge(udist, 0, 3, 1LL);
    assert(changed);
    assert(udist[0][3] == 1);
    assert(udist[3][0] == 1);
    assert(udist[1][3] == 11);
}

void test_topological_sort() {
    Graph<int> g(4);
    g.add_directed_edge(0, 1);
    g.add_directed_edge(0, 2);
    g.add_directed_edge(1, 3);
    g.add_directed_edge(2, 3);

    auto order = m1une::graph::topological_sort(g);
    assert(order.has_value());
    std::vector<int> pos(4);
    for (int i = 0; i < 4; i++) pos[(*order)[i]] = i;
    for (int v = 0; v < 4; v++) {
        for (const auto& e : g[v]) assert(pos[e.from] < pos[e.to]);
    }

    g.add_directed_edge(3, 0);
    assert(!m1une::graph::is_dag(g));
}

void test_scc() {
    Graph<int> g(4);
    g.add_directed_edge(0, 1);
    g.add_directed_edge(1, 0);
    g.add_directed_edge(1, 2);
    g.add_directed_edge(2, 3);
    g.add_directed_edge(3, 2);

    auto scc = m1une::graph::strongly_connected_components(g);
    assert(scc.count == 2);
    assert(scc.same(0, 1));
    assert(scc.same(2, 3));
    assert(!scc.same(0, 2));
    auto dag = scc.dag(g);
    assert(dag.size() == 2);
    assert(dag.edge_count() == 1);
}

void test_lowlink() {
    Graph<int> g(5);
    g.add_edge(0, 1);
    g.add_edge(1, 2);
    g.add_edge(2, 0);
    int b0 = g.add_edge(1, 3);
    int b1 = g.add_edge(3, 4);

    auto res = m1une::graph::lowlink(g);
    assert((res.articulation == std::vector<int>{1, 3}));
    assert((res.bridge_ids == std::vector<int>{b0, b1}));
}

void test_bipartite_and_components() {
    Graph<int> square(4);
    square.add_edge(0, 1);
    square.add_edge(1, 2);
    square.add_edge(2, 3);
    square.add_edge(3, 0);
    auto bp = m1une::graph::bipartite(square);
    assert(bp.is_bipartite);
    assert(bp.color[0] == bp.color[2]);
    assert((bp.left_vertices == std::vector<int>{0, 2}));
    assert((bp.right_vertices == std::vector<int>{1, 3}));
    assert(bp.left_id[2] == 1);
    assert(bp.right_id[3] == 1);
    auto built = m1une::graph::make_bipartite_matching(square);
    assert(built.has_value());
    assert(built->matching.left_size() == 2);
    assert(built->matching.right_size() == 2);
    assert(built->matching.max_matching() == 2);
    for (const auto& p : built->matching.matching()) {
        int u = built->left_vertex(p.left);
        int v = built->right_vertex(p.right);
        assert(bp.color[u] == 0);
        assert(bp.color[v] == 1);
        assert(square.is_edge_alive(built->original_edge(p.edge_id)));
    }

    Graph<int> triangle(3);
    triangle.add_edge(0, 1);
    triangle.add_edge(1, 2);
    triangle.add_edge(2, 0);
    assert(!m1une::graph::is_bipartite(triangle));
    assert(!m1une::graph::make_bipartite_matching(triangle).has_value());

    Graph<int> cc_graph(5);
    cc_graph.add_edge(0, 1);
    cc_graph.add_edge(2, 3);
    auto cc = m1une::graph::connected_components(cc_graph);
    assert(cc.count == 3);
    assert(cc.same(0, 1));
    assert(cc.same(2, 3));
    assert(!cc.same(0, 2));

    Graph<int> directed(2);
    directed.add_directed_edge(1, 0);
    assert(m1une::graph::is_bipartite(directed));
    auto weak = m1une::graph::connected_components(directed);
    assert(weak.count == 1);

    m1une::graph::BipartiteMatching bm(3, 2);
    int e00 = bm.add_edge(0, 0);
    int e10 = bm.add_edge(1, 0);
    int e11 = bm.add_edge(1, 1);
    int e21 = bm.add_edge(2, 1);
    assert(bm.left_size() == 3);
    assert(bm.right_size() == 2);
    assert(bm.edge_count() == 4);
    assert(bm.get_edge(e10).left == 1);
    assert(bm.max_matching() == 2);
    assert(bm.matching_size() == 2);
    auto pairs = bm.matching();
    assert(pairs.size() == 2);
    auto left_match = bm.left_match();
    auto right_match = bm.right_match();
    for (const auto& p : pairs) {
        assert(left_match[p.left] == p.right);
        assert(right_match[p.right] == p.left);
    }

    auto cover = bm.minimum_vertex_cover();
    assert(cover.left.empty());
    assert((cover.right == std::vector<int>{0, 1}));
    assert(cover.size() == 2);
    auto independent = bm.maximum_independent_set();
    assert((independent.left == std::vector<int>{0, 1, 2}));
    assert(independent.right.empty());

    auto edge_cover = bm.minimum_edge_cover();
    assert(edge_cover.has_value());
    assert(edge_cover->size() == 3);
    std::vector<bool> covered_left(3, false), covered_right(2, false);
    for (int id : *edge_cover) {
        auto edge = bm.get_edge(id);
        covered_left[edge.left] = true;
        covered_right[edge.right] = true;
    }
    assert((covered_left == std::vector<bool>{true, true, true}));
    assert((covered_right == std::vector<bool>{true, true}));

    bm.erase_edge(e11);
    bm.erase_edge(e21);
    assert(!bm.is_edge_alive(e21));
    assert(bm.edges().size() == 2);
    assert(bm.edges(true).size() == 4);
    assert(bm.max_matching() == 1);
    bm.revive_edge(e21);
    assert(bm.max_matching() == 2);

    m1une::graph::BipartiteMatching isolated(1, 1);
    assert(!isolated.minimum_edge_cover().has_value());

    (void)e00;
}

void test_general_matching() {
    m1une::graph::GeneralMatching blossom(6);
    int e01 = blossom.add_edge(0, 1);
    int e12 = blossom.add_edge(1, 2);
    int e23 = blossom.add_edge(2, 3);
    int e34 = blossom.add_edge(3, 4);
    int e40 = blossom.add_edge(4, 0);
    int e15 = blossom.add_edge(1, 5);
    (void)e12;
    (void)e23;
    (void)e34;
    (void)e40;

    assert(blossom.size() == 6);
    assert(blossom.edge_count() == 6);
    assert(blossom.get_edge(e01).other(0) == 1);
    assert(blossom.max_matching() == 3);
    assert(blossom.matching_size() == 3);
    auto mate = blossom.mate();
    auto mate_edge = blossom.mate_edge();
    for (int v = 0; v < 6; v++) {
        assert(mate[v] != -1);
        assert(mate[mate[v]] == v);
        assert(mate_edge[v] != -1);
    }
    auto pairs = blossom.matching();
    assert(pairs.size() == 3);
    for (const auto& p : pairs) {
        assert(mate[p.from] == p.to);
        assert(mate[p.to] == p.from);
    }

    auto edge_cover = blossom.minimum_edge_cover();
    assert(edge_cover.has_value());
    assert(edge_cover->size() == 3);
    std::vector<bool> covered(6, false);
    for (int id : *edge_cover) {
        auto edge = blossom.get_edge(id);
        covered[edge.from] = true;
        covered[edge.to] = true;
    }
    assert((covered == std::vector<bool>{true, true, true, true, true, true}));

    blossom.erase_edge(e15);
    assert(!blossom.is_edge_alive(e15));
    assert(blossom.edges().size() == 5);
    assert(blossom.edges(true).size() == 6);
    assert(blossom.max_matching() == 2);
    assert(!blossom.minimum_edge_cover().has_value());
    blossom.revive_edge(e15);
    assert(blossom.max_matching() == 3);

    m1une::graph::GeneralMatching tricky(6);
    tricky.add_edge(0, 1);
    tricky.add_edge(0, 4);
    tricky.add_edge(1, 2);
    tricky.add_edge(3, 4);
    tricky.add_edge(3, 5);
    tricky.add_edge(4, 5);
    assert(tricky.max_matching() == 3);
    for (const auto& p : tricky.matching()) {
        auto e = tricky.get_edge(p.edge_id);
        assert((e.from == p.from && e.to == p.to) || (e.from == p.to && e.to == p.from));
    }

    m1une::graph::GeneralMatching parallel(4);
    int p01_removed = parallel.add_edge(0, 1);
    parallel.add_edge(0, 1);
    parallel.add_edge(2, 3);
    parallel.erase_edge(p01_removed);
    assert(parallel.max_matching() == 2);
    auto parallel_mate_edge = parallel.mate_edge();
    for (int v = 0; v < 4; v++) assert(parallel.is_edge_alive(parallel_mate_edge[v]));

    m1une::graph::GeneralMatching path(3);
    path.add_edge(0, 1);
    path.add_edge(1, 2);
    auto path_cover = path.minimum_edge_cover();
    assert(path_cover.has_value());
    assert(path_cover->size() == 2);

    m1une::graph::GeneralMatching bipartite_general(8);
    int bg03 = bipartite_general.add_edge(0, 3);
    bipartite_general.add_edge(0, 7);
    bipartite_general.add_edge(1, 4);
    bipartite_general.add_edge(2, 5);
    bipartite_general.add_edge(6, 3);
    bipartite_general.add_edge(2, 7);
    bipartite_general.erase_edge(bg03);
    assert(bipartite_general.max_matching() == 4);
    auto bipartite_mate = bipartite_general.mate();
    for (int v = 0; v < 8; v++) {
        assert(bipartite_mate[v] != -1);
        assert(bipartite_mate[bipartite_mate[v]] == v);
    }

    m1une::graph::GeneralMatching isolated(1);
    assert(!isolated.minimum_edge_cover().has_value());

    Graph<int> g(4);
    int g01 = g.add_edge(0, 1);
    int g12 = g.add_edge(1, 2);
    int g20 = g.add_edge(2, 0);
    int g23 = g.add_edge(2, 3);
    (void)g01;
    (void)g12;
    (void)g20;
    auto built = m1une::graph::make_general_matching(g);
    assert(built.matching.max_matching() == 2);
    for (const auto& p : built.matching.matching()) {
        assert(g.is_edge_alive(built.original_edge(p.edge_id)));
    }
    assert(g.is_edge_alive(g23));
}

void test_maximum_clique_and_independent_set() {
    Graph<int> g(7);
    int removed_clique_edge = -1;
    for (int i = 0; i < 4; i++) {
        for (int j = i + 1; j < 4; j++) {
            int id = g.add_edge(i, j);
            if (i == 0 && j == 2) removed_clique_edge = id;
        }
    }
    g.add_edge(4, 0);
    g.add_edge(4, 1);
    g.add_edge(5, 1);
    g.add_edge(5, 2);

    auto clique = m1une::graph::maximum_clique(g);
    assert(clique.size() == 4);
    assert((clique.vertices == std::vector<int>{0, 1, 2, 3}));
    assert(m1une::graph::is_clique(g, clique.vertices));
    assert(m1une::graph::maximum_clique_size(g) == 4);

    auto independent = m1une::graph::maximum_independent_set(g);
    assert(independent.size() == 4);
    assert((independent.vertices == std::vector<int>{3, 4, 5, 6}));
    assert(m1une::graph::is_independent_set(g, independent.vertices));
    assert(m1une::graph::maximum_independent_set_size(g) == 4);

    auto cover = m1une::graph::minimum_vertex_cover(g);
    assert(cover.size() == 3);
    assert((cover.vertices == std::vector<int>{0, 1, 2}));
    assert(m1une::graph::is_vertex_cover(g, cover.vertices));
    assert(m1une::graph::minimum_vertex_cover_size(g) == 3);

    assert(!m1une::graph::is_clique(g, std::vector<int>{0, 2, 4}));
    assert(!m1une::graph::is_independent_set(g, std::vector<int>{4, 0}));
    assert(!m1une::graph::is_vertex_cover(g, std::vector<int>{0, 1}));

    g.erase_edge(removed_clique_edge);
    assert(m1une::graph::maximum_clique_size(g) == 3);
    g.revive_edge(removed_clique_edge);
    assert(m1une::graph::maximum_clique_size(g) == 4);

    Graph<int> directed(3);
    directed.add_directed_edge(0, 1);
    directed.add_directed_edge(1, 2);
    auto directed_clique = m1une::graph::maximum_clique(directed);
    auto directed_independent = m1une::graph::maximum_independent_set(directed);
    auto directed_cover = m1une::graph::minimum_vertex_cover(directed);
    assert(directed_clique.size() == 2);
    assert(directed_independent.size() == 2);
    assert((directed_independent.vertices == std::vector<int>{0, 2}));
    assert((directed_cover.vertices == std::vector<int>{1}));

    Graph<int> empty(4);
    assert(m1une::graph::maximum_clique_size(empty) == 1);
    assert(m1une::graph::maximum_independent_set_size(empty) == 4);
    assert(m1une::graph::minimum_vertex_cover(empty).empty());

    Graph<int> path(6);
    for (int i = 0; i + 1 < 6; i++) path.add_edge(i, i + 1);
    assert(m1une::graph::maximum_independent_set_size(path) == 3);
    assert(m1une::graph::minimum_vertex_cover_size(path) == 3);

    Graph<int> cycle(5);
    for (int i = 0; i < 5; i++) cycle.add_edge(i, (i + 1) % 5);
    assert(m1une::graph::maximum_independent_set_size(cycle) == 2);
    assert(m1une::graph::minimum_vertex_cover_size(cycle) == 3);

    Graph<int> none(0);
    assert(m1une::graph::maximum_clique(none).empty());
    assert(m1une::graph::maximum_independent_set(none).empty());
    assert(m1une::graph::minimum_vertex_cover(none).empty());
}

void test_cycle_detection() {
    Graph<int> dg(3);
    dg.add_directed_edge(0, 1);
    dg.add_directed_edge(1, 2);
    dg.add_directed_edge(2, 0);
    auto directed = m1une::graph::find_directed_cycle(dg);
    assert(!directed.empty());
    assert(directed.vertices.front() == directed.vertices.back());
    assert(directed.edge_ids.size() + 1 == directed.vertices.size());

    Graph<int> ug(4);
    ug.add_edge(0, 1);
    ug.add_edge(1, 2);
    ug.add_edge(2, 0);
    ug.add_edge(2, 3);
    auto undirected = m1une::graph::find_undirected_cycle(ug);
    assert(!undirected.empty());
    assert(undirected.vertices.front() == undirected.vertices.back());
}

void test_kruskal() {
    Graph<long long> g(4);
    g.add_edge(0, 1, 1);
    g.add_edge(1, 2, 2);
    g.add_edge(2, 3, 3);
    g.add_edge(0, 3, 10);
    g.add_edge(0, 2, 4);

    auto mst = m1une::graph::kruskal(g);
    assert(mst.cost == 6);
    assert(mst.edges.size() == 3);
    assert(mst.components == 1);
    assert(mst.is_spanning_tree(g.size()));
}

void test_grid() {
    m1une::graph::Grid grid(3, 4);
    assert(grid.height() == 3);
    assert(grid.width() == 4);
    assert(grid.size() == 12);
    assert(grid.inside(2, 3));
    assert(!grid.inside(3, 0));
    assert(grid.id(2, 3) == 11);
    assert(grid.pos(6) == std::make_pair(1, 2));

    auto adj4 = grid.adj4(0, 0);
    std::vector<std::pair<int, int>> expected_adj4 = {
        std::pair<int, int>{0, 1},
        std::pair<int, int>{1, 0},
    };
    assert(adj4 == expected_adj4);

    auto adj8 = grid.adj8(1, 1);
    assert(adj8.size() == 8);
    auto adj4_ids = grid.adj4_ids(grid.id(1, 1));
    std::set<int> expected_ids = {grid.id(0, 1), grid.id(1, 2), grid.id(2, 1), grid.id(1, 0)};
    assert(std::set<int>(adj4_ids.begin(), adj4_ids.end()) == expected_ids);

    std::vector<std::string> s = {
        "....",
        ".##.",
        "....",
    };
    auto passable = [&](int i, int j) {
        return s[i][j] != '#';
    };

    auto g4 = grid.graph4(passable);
    assert(g4.size() == grid.size());
    assert(g4[grid.id(1, 1)].empty());
    auto res = m1une::graph::bfs(g4, grid.id(0, 0));
    assert(res.dist[grid.id(2, 3)] == 5);
    assert(res.dist[grid.id(1, 1)] == -1);

    auto g8 = grid.graph8(passable);
    auto res8 = m1une::graph::bfs(g8, grid.id(0, 0));
    assert(res8.dist[grid.id(2, 3)] == 4);

    auto all4 = grid.graph4();
    assert(all4.edge_count() == 17);
}

int main() {
    m1une::utilities::FastInput fast_input;
    m1une::utilities::FastOutput fast_output;

    test_graph_container();
    test_edge_alive();
    test_bfs();
    test_dijkstra();
    test_zero_one_bfs();
    test_bellman_ford();
    test_dag_shortest_path();
    test_warshall_floyd();
    test_topological_sort();
    test_scc();
    test_lowlink();
    test_bipartite_and_components();
    test_general_matching();
    test_maximum_clique_and_independent_set();
    test_cycle_detection();
    test_kruskal();
    test_grid();
    long long a, b;
    fast_input >> a >> b;
    fast_output << a + b << '\n';
}
Back to top page