m1une's library

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

View on GitHub

:heavy_check_mark: verify/graph/tree/tree_algorithms.test.cpp

Depends on

Code

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

#include <algorithm>
#include <array>
#include <cassert>
#include "../../../utilities/fast_io.hpp"
#include <numeric>
#include <random>
#include <set>
#include <vector>

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

using m1une::graph::Graph;

template <class Hld>
std::vector<int> expand_segments(const Hld& hld, const std::vector<m1une::tree::HldPathSegment>& segments) {
    std::vector<int> result;
    for (auto seg : segments) {
        if (seg.reversed) {
            for (int i = seg.r - 1; i >= seg.l; i--) result.push_back(hld.order[i]);
        } else {
            for (int i = seg.l; i < seg.r; i++) result.push_back(hld.order[i]);
        }
    }
    return result;
}

Graph<long long> sample_tree() {
    Graph<long long> g(7);
    g.add_edge(0, 1, 3);
    g.add_edge(0, 2, 2);
    g.add_edge(1, 3, 4);
    g.add_edge(1, 4, 1);
    g.add_edge(2, 5, 6);
    g.add_edge(5, 6, 2);
    return g;
}

void test_rooted_tree() {
    auto g = sample_tree();
    m1une::tree::RootedTree<long long> tree(g, 0);

    assert(tree.size() == 7);
    assert(!tree.empty());
    assert(tree.root == 0);
    assert(tree.parent[0] == -1);
    assert(tree.parent[3] == 1);
    assert(tree.depth[6] == 3);
    assert(tree.dist[6] == 10);
    assert(tree.subtree_size[0] == 7);
    assert(tree.subtree_size[1] == 3);
    assert(tree.is_ancestor(1, 4));
    assert(!tree.is_ancestor(2, 4));
    assert(tree.in_subtree(4, 1));

    assert(tree.kth_ancestor(6, 0) == 6);
    assert(tree.kth_ancestor(6, 1) == 5);
    assert(tree.kth_ancestor(6, 3) == 0);
    assert(tree.kth_ancestor(6, 4) == -1);
    assert(tree.lca(3, 4) == 1);
    assert(tree.lca(3, 6) == 0);
    assert(tree.dist_edges(3, 6) == 5);
    assert(tree.dist_cost(3, 6) == 17);
    assert(tree.jump(3, 6, 0) == 3);
    assert(tree.jump(3, 6, 1) == 1);
    assert(tree.jump(3, 6, 2) == 0);
    assert(tree.jump(3, 6, 3) == 2);
    assert(tree.jump(3, 6, 5) == 6);
    assert(tree.jump(3, 6, 6) == -1);

    std::vector<int> expected_path = {3, 1, 0, 2, 5, 6};
    assert(tree.path(3, 6) == expected_path);
    std::vector<int> expected_edges = {2, 0, 1, 4, 5};
    assert(tree.path_edges(3, 6) == expected_edges);

    auto [l, r] = tree.subtree_range(1);
    assert(r - l == 3);
    auto sub = tree.subtree_vertices(1);
    std::sort(sub.begin(), sub.end());
    assert((sub == std::vector<int>{1, 3, 4}));
}

void test_euler_tour() {
    auto g = sample_tree();
    m1une::tree::EulerTour<long long> tour(g, 0);

    std::vector<int> expected_order = {0, 1, 3, 4, 2, 5, 6};
    assert(tour.size() == 7);
    assert(tour.visited_size() == 7);
    assert(tour.root == 0);
    assert(tour.order == expected_order);
    assert(tour.parent[6] == 5);
    assert(tour.parent_edge[6] == 5);
    assert(tour.depth[6] == 3);
    assert(tour.dist[6] == 10);
    assert(tour.subtree_size[1] == 3);
    assert(tour.is_ancestor(1, 4));
    assert(!tour.is_ancestor(2, 4));

    auto [l, r] = tour.subtree_range(1);
    assert(l == 1);
    assert(r == 4);
    auto [el, er] = tour.subtree_range(1, true);
    assert(el == 2);
    assert(er == 4);

    std::vector<int> subtree = tour.subtree_vertices(1);
    std::sort(subtree.begin(), subtree.end());
    std::vector<int> expected_subtree = {1, 3, 4};
    assert(subtree == expected_subtree);
}

void test_sparse_table_lca() {
    auto g = sample_tree();
    m1une::tree::RootedTree<long long> tree(g, 0);
    m1une::tree::SparseTableLca<long long> lca(g, 0);

    assert(lca.size() == 7);
    assert(!lca.empty());
    assert(lca.root == 0);
    assert(lca.parent[0] == -1);
    assert(lca.parent[6] == 5);
    assert(lca.depth[6] == 3);
    assert(lca.dist[6] == 10);
    assert(lca.euler.size() == 13);
    assert(lca.first[0] == 0);
    assert(lca.is_ancestor(2, 6));
    assert(!lca.is_ancestor(1, 6));
    assert(lca.in_subtree(6, 2));

    for (int u = 0; u < 7; u++) {
        for (int v = 0; v < 7; v++) {
            assert(lca.lca(u, v) == tree.lca(u, v));
            assert(lca.dist_edges(u, v) == tree.dist_edges(u, v));
            assert(lca.dist_cost(u, v) == tree.dist_cost(u, v));
        }
    }

    auto [l, r] = lca.subtree_range(2);
    assert(r - l == 3);
    std::vector<int> subtree;
    for (int i = l; i < r; i++) subtree.push_back(lca.order[i]);
    std::sort(subtree.begin(), subtree.end());
    assert((subtree == std::vector<int>{2, 5, 6}));
}

void test_virtual_tree() {
    auto graph = sample_tree();
    m1une::tree::VirtualTree<long long> builder(graph, 0);

    auto virtual_tree = builder.build(std::vector<int>{3, 4, 6, 3});
    std::vector<int> expected_vertices = {0, 1, 3, 4, 6};
    assert(virtual_tree.vertex == expected_vertices);
    assert(virtual_tree.parent == std::vector<int>({-1, 0, 1, 1, 0}));
    assert(virtual_tree.parent_edge_count == std::vector<int>({0, 1, 1, 1, 3}));
    assert(virtual_tree.parent_cost == std::vector<long long>({0, 3, 4, 1, 10}));
    assert(virtual_tree.is_key == std::vector<bool>({false, false, true, true, true}));
    assert(virtual_tree.children[0] == std::vector<int>({1, 4}));
    assert(virtual_tree.children[1] == std::vector<int>({2, 3}));
    assert(virtual_tree.root() == 0);
    assert(virtual_tree.root_vertex() == 0);
    assert(virtual_tree.edge_count() == 4);

    auto singleton = builder.build(std::vector<int>{5, 5});
    assert(singleton.size() == 1);
    assert(singleton.vertex[0] == 5);
    assert(singleton.parent[0] == -1);
    assert(singleton.is_key[0]);

    auto empty = builder.build({});
    assert(empty.empty());
    assert(empty.root() == -1);
    assert(empty.root_vertex() == -1);
    assert(empty.edge_count() == 0);

    std::mt19937 random(123456789);
    for (int test = 0; test < 100; test++) {
        int n = 1 + random() % 200;
        Graph<long long> random_graph(n);
        for (int v = 1; v < n; v++) {
            int parent = random() % v;
            long long cost = 1 + random() % 1000000;
            random_graph.add_edge(parent, v, cost);
        }
        m1une::tree::VirtualTree<long long> random_builder(random_graph, 0);
        const auto& lca = random_builder.lca_data();
        for (int query = 0; query < 100; query++) {
            int k = random() % (2 * n + 1);
            std::vector<int> keys(k);
            for (int& v : keys) v = random() % n;
            auto result = random_builder.build(keys);

            std::sort(keys.begin(), keys.end(), [&](int u, int v) {
                return lca.tin[u] < lca.tin[v];
            });
            keys.erase(std::unique(keys.begin(), keys.end()), keys.end());
            std::vector<int> expected = keys;
            for (int i = 1; i < int(keys.size()); i++) expected.push_back(lca.lca(keys[i - 1], keys[i]));
            std::sort(expected.begin(), expected.end(), [&](int u, int v) {
                return lca.tin[u] < lca.tin[v];
            });
            expected.erase(std::unique(expected.begin(), expected.end()), expected.end());
            assert(result.vertex == expected);

            int key_index = 0;
            for (int i = 0; i < result.size(); i++) {
                while (key_index < int(keys.size()) && lca.tin[keys[key_index]] < lca.tin[result.vertex[i]]) {
                    key_index++;
                }
                bool is_key = key_index < int(keys.size()) && keys[key_index] == result.vertex[i];
                assert(result.is_key[i] == is_key);
                if (i == 0) {
                    assert(result.parent[i] == -1);
                    continue;
                }
                int parent = result.parent[i];
                assert(0 <= parent && parent < i);
                assert(lca.is_ancestor(result.vertex[parent], result.vertex[i]));
                assert(result.parent_edge_count[i] == lca.dist_edges(result.vertex[parent], result.vertex[i]));
                assert(result.parent_cost[i] == lca.dist_cost(result.vertex[parent], result.vertex[i]));
                for (int j = parent + 1; j < i; j++) {
                    assert(!lca.is_ancestor(result.vertex[j], result.vertex[i]));
                }
            }
        }
    }
}

void test_hld() {
    auto g = sample_tree();
    m1une::tree::HeavyLightDecomposition<long long> hld(g, 0);

    assert(hld.size() == 7);
    assert(hld.root == 0);
    assert(hld.lca(3, 4) == 1);
    assert(hld.lca(3, 6) == 0);
    assert(hld.dist_edges(3, 6) == 5);
    assert(hld.dist_cost(3, 6) == 17);
    assert(hld.kth_ancestor(6, 2) == 2);
    assert(hld.kth_ancestor(6, 4) == -1);
    assert(hld.jump(3, 6, 4) == 5);

    std::vector<int> expected_path = {3, 1, 0, 2, 5, 6};
    assert(expand_segments(hld, hld.path_segments(3, 6)) == expected_path);
    std::vector<int> expected_edge_vertices = {3, 1, 2, 5, 6};
    assert(expand_segments(hld, hld.path_segments(3, 6, true)) == expected_edge_vertices);

    int segment_count = 0;
    hld.for_each_path(3, 6, [&](int l, int r, bool) {
        assert(l < r);
        segment_count++;
    });
    assert(segment_count == int(hld.path_segments(3, 6).size()));

    auto [vl, vr] = hld.subtree_range(1);
    std::vector<int> subtree;
    for (int i = vl; i < vr; i++) subtree.push_back(hld.order[i]);
    std::sort(subtree.begin(), subtree.end());
    assert((subtree == std::vector<int>{1, 3, 4}));

    auto [el, er] = hld.subtree_range(1, true);
    std::vector<int> edge_subtree;
    for (int i = el; i < er; i++) edge_subtree.push_back(hld.order[i]);
    std::sort(edge_subtree.begin(), edge_subtree.end());
    assert((edge_subtree == std::vector<int>{3, 4}));
}

void test_diameter() {
    auto g = sample_tree();
    auto diameter = m1une::tree::tree_diameter(g);

    assert(!diameter.empty());
    assert(diameter.cost == 17);
    assert(diameter.edge_count == 5);
    assert(diameter.from == diameter.vertices.front());
    assert(diameter.to == diameter.vertices.back());
    std::set<int> endpoints = {diameter.from, diameter.to};
    assert((endpoints == std::set<int>{3, 6}));

    g.erase_edge(1);
    auto split = m1une::tree::tree_diameter(g);
    assert(split.cost == 8);
    assert(split.edge_count == 2);
}

void test_rerooting() {
    auto g = sample_tree();
    auto component_size = m1une::tree::rerooting_dp(
        g,
        0,
        [](int a, int b) { return a + b; },
        [](int acc, int) { return acc + 1; },
        [](int dp, const auto&) { return dp; });
    assert(component_size == std::vector<int>(7, 7));

    auto eccentricity_edges = m1une::tree::rerooting_dp(
        g,
        0,
        [](int a, int b) { return std::max(a, b); },
        [](int acc, int) { return acc; },
        [](int dp, const auto&) { return dp + 1; });
    assert(eccentricity_edges[0] == 3);
    assert(eccentricity_edges[3] == 5);
    assert(eccentricity_edges[6] == 5);

    auto eccentricity_cost = m1une::tree::rerooting_dp(
        g,
        0LL,
        [](long long a, long long b) { return std::max(a, b); },
        [](long long acc, int) { return acc; },
        [](long long dp, const auto& e) { return dp + e.cost; });
    assert(eccentricity_cost[0] == 10);
    assert(eccentricity_cost[3] == 17);
    assert(eccentricity_cost[6] == 17);
}

struct DistancePath {
    long long count;
    long long sum;
    long long length;
};

struct DistancePoint {
    long long count;
    long long sum;
};

struct ColorVertex {
    long long weight;
    int color;
};

struct ColorPath {
    int first_color;
    int last_color;
    long long first_sum;
    long long last_sum;
    bool connected;
};

struct ColorPoint {
    std::array<long long, 2> sum;
};

void test_static_top_tree() {
    auto g = sample_tree();
    std::vector<long long> values = {1, 2, 3, 4, 5, 6, 7};

    auto vertex_sum = m1une::tree::StaticTopTree(
        g,
        values,
        0LL,
        [](long long top, long long bottom, const auto&) {
            return top + bottom;
        },
        [](long long a, long long b) {
            return a + b;
        },
        [](long long path, const auto&) {
            return path;
        },
        [](long long side, long long value, int) {
            return side + value;
        });

    assert(vertex_sum.size() == 7);
    assert(vertex_sum.root() == 0);
    assert(vertex_sum.all_prod() == 28);
    assert(vertex_sum.query() == 28);
    assert(vertex_sum.get(3) == 4);
    assert(vertex_sum.height() > 0);
    vertex_sum.set(3, 100);
    assert(vertex_sum[3] == 100);
    assert(vertex_sum.all_prod() == 124);

    auto root_distance_sum = m1une::tree::StaticTopTree(
        g,
        std::vector<int>(7, 0),
        DistancePoint{0, 0},
        [](DistancePath top, DistancePath bottom, const auto& e) {
            long long shift = top.length + e.cost;
            return DistancePath{top.count + bottom.count, top.sum + bottom.sum + bottom.count * shift,
                                top.length + e.cost + bottom.length};
        },
        [](DistancePoint a, DistancePoint b) {
            return DistancePoint{a.count + b.count, a.sum + b.sum};
        },
        [](DistancePath path, const auto& e) {
            return DistancePoint{path.count, path.sum + path.count * e.cost};
        },
        [](DistancePoint side, int, int) {
            return DistancePath{side.count + 1, side.sum, 0};
        });

    assert(root_distance_sum.all_prod().count == 7);
    assert(root_distance_sum.all_prod().sum == 34);
    root_distance_sum.set_edge_cost(0, 10);
    assert(root_distance_sum.all_prod().sum == 55);
    root_distance_sum.set_edge_cost(1, 10);
    assert(root_distance_sum.all_prod().sum == 79);
}

void test_rerooting_static_top_tree() {
    Graph<long long> g(3);
    int e01 = g.add_edge(0, 1, 2);
    int e12 = g.add_edge(1, 2, 5);
    std::vector<long long> weights = {1, 1, 1};

    auto stt = m1une::tree::RerootingStaticTopTree(
        g,
        weights,
        DistancePoint{0, 0},
        [](DistancePath upper, DistancePath lower, const auto& e) {
            long long shift = upper.length + e.cost;
            return DistancePath{upper.count + lower.count, upper.sum + lower.sum + lower.count * shift,
                                upper.length + e.cost + lower.length};
        },
        [](DistancePath lower, DistancePath upper, const auto& e) {
            long long shift = lower.length + e.cost;
            return DistancePath{lower.count + upper.count, lower.sum + upper.sum + upper.count * shift,
                                lower.length + e.cost + upper.length};
        },
        [](DistancePoint a, DistancePoint b) {
            return DistancePoint{a.count + b.count, a.sum + b.sum};
        },
        [](DistancePath path, const auto& e) {
            return DistancePoint{path.count, path.sum + path.count * e.cost};
        },
        [](DistancePath path, const auto& e) {
            return DistancePoint{path.count, path.sum + path.count * e.cost};
        },
        [](DistancePoint side, long long weight, int) {
            return DistancePath{side.count + weight, side.sum, 0};
        });

    assert(stt.size() == 3);
    assert(stt.root() == 0);
    assert(stt.node_count() >= 3);
    assert(stt.height() > 0);
    assert(stt.all_prod_down().count == 3);
    assert(stt.all_prod_down().sum == 9);
    assert(stt.all_prod_down().length == 7);
    assert(stt.all_prod_up().count == 3);
    assert(stt.all_prod_up().sum == 12);
    assert(stt.all_prod_up().length == 7);

    int root_node = stt.root_node();
    assert(stt.path_down(root_node).sum == stt.all_prod_down().sum);
    assert(stt.path_up(root_node).sum == stt.all_prod_up().sum);
    int one_node = stt.vertex_node(1);
    assert(stt.node(one_node).type == m1une::tree::internal::RerootingStaticTopTreeNodeType::AddVertex);
    assert(stt.parent_node(root_node) == -1);
    assert(stt.point_id().count == 0);
    assert(stt.local_point_node(1) == -1);
    assert(stt.local_point(1).count == 0);

    using RerootingStepType = decltype(stt)::step_type;
    using RerootingNodeType = decltype(stt)::node_type;
    auto steps = stt.rerooting_steps(1);
    std::vector<decltype(stt)::RerootingStep> streamed_steps;
    stt.for_each_rerooting_step(1, [&](const auto& step) {
        streamed_steps.push_back(step);
    });
    assert(streamed_steps.size() == steps.size());
    int cur = one_node;
    for (int i = 0; i < int(steps.size()); i++) {
        const auto& step = steps[i];
        const auto& streamed = streamed_steps[i];
        assert(streamed.type == step.type);
        assert(streamed.node == step.node);
        assert(streamed.sibling == step.sibling);
        assert(streamed.vertex == step.vertex);
        assert(streamed.edge.from == step.edge.from);
        assert(streamed.edge.to == step.edge.to);
        assert(streamed.edge.id == step.edge.id);

        assert(stt.parent_node(cur) == step.node);
        const auto& parent = stt.node(step.node);
        if (step.type == RerootingStepType::CompressLower) {
            assert(parent.type == RerootingNodeType::Compress);
            assert(parent.left == cur);
            assert(parent.right == step.sibling);
            assert(stt.node(step.sibling).path_down.has_value());
        } else if (step.type == RerootingStepType::CompressUpper) {
            assert(parent.type == RerootingNodeType::Compress);
            assert(parent.right == cur);
            assert(parent.left == step.sibling);
            assert(stt.node(step.sibling).path_up.has_value());
        } else if (step.type == RerootingStepType::RakeLeft) {
            assert(parent.type == RerootingNodeType::Rake);
            assert(parent.right == cur);
            assert(parent.left == step.sibling);
            assert(stt.node(step.sibling).point.has_value());
        } else if (step.type == RerootingStepType::RakeRight) {
            assert(parent.type == RerootingNodeType::Rake);
            assert(parent.left == cur);
            assert(parent.right == step.sibling);
            assert(stt.node(step.sibling).point.has_value());
        } else if (step.type == RerootingStepType::AddEdge) {
            assert(parent.type == RerootingNodeType::AddEdge);
            assert(parent.left == cur);
        } else {
            assert(step.type == RerootingStepType::AddVertex);
            assert(parent.type == RerootingNodeType::AddVertex);
            assert(parent.left == cur);
            assert(parent.vertex == step.vertex);
        }
        cur = step.node;
    }
    assert(cur == stt.root_node());

    auto edge = m1une::graph::Edge<long long>(0, 1, 2, e01);
    auto reversed = decltype(stt)::reverse_edge(edge);
    assert(reversed.from == 1);
    assert(reversed.to == 0);
    DistancePath one = stt.add_vertex(stt.point_id(), 1LL, 0);
    assert(one.count == 1);
    assert(one.sum == 0);
    auto down_point = stt.add_edge_down(one, edge);
    auto up_point = stt.add_edge_up(one, reversed);
    assert(down_point.sum == 2);
    assert(up_point.sum == 2);
    auto raked = stt.rake(down_point, up_point);
    assert(raked.count == 2);
    assert(raked.sum == 4);
    assert(stt.compress_down(one, one, edge).sum == 2);
    assert(stt.compress_up(one, one, reversed).sum == 2);

    stt.set_edge_cost(e01, 10);
    assert(stt.all_prod_down().count == 3);
    assert(stt.all_prod_down().sum == 25);
    assert(stt.all_prod_down().length == 15);
    assert(stt.all_prod_up().sum == 20);
    assert(stt.all_prod_up().length == 15);

    stt.set(0, 3);
    assert(stt[0] == 3);
    assert(stt.all_prod_down().count == 5);
    assert(stt.all_prod_down().sum == 25);
    assert(stt.all_prod_up().count == 5);
    assert(stt.all_prod_up().sum == 50);

    stt.set_edge_cost(e12, 1);
    assert(stt.all_prod_down().sum == 21);
    assert(stt.all_prod_up().sum == 34);
}

void test_rerooting_static_top_tree_vertex_component() {
    auto g = sample_tree();
    std::vector<ColorVertex> values = {
        ColorVertex{1, 0},      ColorVertex{10, 0},     ColorVertex{100, 1},    ColorVertex{1000, 0},
        ColorVertex{10000, 1},  ColorVertex{100000, 1}, ColorVertex{1000000, 1},
    };

    auto compress = [](ColorPath a, ColorPath b, const auto&) {
        bool join = a.last_color == b.first_color;
        ColorPath res{a.first_color, b.last_color, a.first_sum, b.last_sum, false};
        if (join && a.connected) res.first_sum += b.first_sum;
        if (join && b.connected) res.last_sum += a.last_sum;
        res.connected = a.connected && b.connected && join;
        return res;
    };
    auto rake = [](ColorPoint a, ColorPoint b) {
        return ColorPoint{a.sum[0] + b.sum[0], a.sum[1] + b.sum[1]};
    };
    auto add_edge = [](ColorPath path, const auto&) {
        ColorPoint res{};
        res.sum[path.first_color] = path.first_sum;
        return res;
    };
    auto add_vertex = [](ColorPoint side, ColorVertex value, int) {
        long long sum = value.weight + side.sum[value.color];
        return ColorPath{value.color, value.color, sum, sum, true};
    };

    auto stt = m1une::tree::RerootingStaticTopTree(
        g, values, ColorPoint{0, 0}, compress, compress, rake, add_edge, add_edge, add_vertex);

    using ColorStt = decltype(stt);
    struct QueryFolder {
        const ColorStt& stt;
        const std::vector<ColorVertex>& values;
        int color = 0;
        long long answer = 0;
        bool touches_top = false;
        bool touches_bottom = false;
        bool pending_open = false;
        ColorPoint pending{};

        void start(int v, const ColorVertex& value, const ColorPoint& local) {
            color = value.color;
            answer = value.weight + local.sum[color];
            touches_top = true;
            touches_bottom = true;
            pending_open = false;
            pending = stt.point_id();
            assert(values[v].color == color);
        }

        void compress_lower(const ColorPath& lower, ColorStt::edge_type) {
            bool connect = touches_bottom && lower.first_color == color;
            if (connect) answer += lower.first_sum;
            touches_bottom = connect && lower.connected;
        }

        void compress_upper(const ColorPath& upper, ColorStt::edge_type) {
            bool connect = touches_top && upper.first_color == color;
            if (connect) answer += upper.first_sum;
            touches_top = connect && upper.connected;
        }

        void add_edge(ColorStt::edge_type) {
            pending_open = touches_top;
            pending = stt.point_id();
        }

        void rake_left(const ColorPoint& point) {
            if (pending_open) pending = stt.rake(point, pending);
        }

        void rake_right(const ColorPoint& point) {
            if (pending_open) pending = stt.rake(pending, point);
        }

        void add_vertex(int, const ColorVertex& value) {
            if (pending_open && value.color == color) {
                answer += value.weight + pending.sum[color];
                touches_top = true;
                touches_bottom = true;
            } else {
                touches_top = false;
                touches_bottom = false;
            }
            pending_open = false;
            pending = stt.point_id();
        }

        long long result() const {
            return answer;
        }
    };

    auto query = [&](int v) {
        return stt.fold_rerooting(v, QueryFolder{stt, values});
    };

    auto brute = [&](int start) {
        int color = values[start].color;
        long long answer = 0;
        std::vector<char> seen(g.size(), false);
        std::vector<int> stack = {start};
        seen[start] = true;
        while (!stack.empty()) {
            int v = stack.back();
            stack.pop_back();
            answer += values[v].weight;
            for (const auto& e : g[v]) {
                if (seen[e.to] || values[e.to].color != color) continue;
                seen[e.to] = true;
                stack.push_back(e.to);
            }
        }
        return answer;
    };

    auto check_all = [&]() {
        for (int v = 0; v < g.size(); v++) assert(query(v) == brute(v));
    };

    check_all();
    values[2].color ^= 1;
    stt.set(2, values[2]);
    check_all();
    values[5].weight += 7;
    stt.set(5, values[5]);
    check_all();
    values[1].color ^= 1;
    stt.set(1, values[1]);
    check_all();
    values[4].weight += 11;
    stt.set(4, values[4]);
    check_all();
}

void test_centroid_decomposition() {
    auto g = sample_tree();
    m1une::tree::CentroidDecomposition<long long> cd(g);

    assert(cd.size() == 7);
    assert(!cd.empty());
    assert(cd.root() == 0);
    assert(cd.roots == std::vector<int>{0});
    assert(cd.parent[cd.root()] == -1);
    assert(cd.depth[cd.root()] == 0);
    assert(cd.order.size() == 7);

    int child_count = 0;
    for (const auto& ch : cd.children) child_count += int(ch.size());
    assert(child_count == 6);
    for (int v = 0; v < 7; v++) {
        if (v == cd.root()) continue;
        assert(cd.parent[v] != -1);
        assert(cd.depth[v] == cd.depth[cd.parent[v]] + 1);
    }
}

void test_forest() {
    Graph<int> g(4);
    g.add_edge(0, 1, 5);
    g.add_edge(2, 3, 7);

    auto diameter = m1une::tree::tree_diameter(g);
    assert(diameter.cost == 7);
    assert(diameter.edge_count == 1);

    auto component_size = m1une::tree::rerooting_dp(
        g,
        0,
        [](int a, int b) { return a + b; },
        [](int acc, int) { return acc + 1; },
        [](int dp, const auto&) { return dp; });
    assert(component_size == std::vector<int>(4, 2));

    m1une::tree::CentroidDecomposition<int> cd(g);
    assert(cd.roots.size() == 2);
    assert(cd.order.size() == 4);
}

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

    test_rooted_tree();
    test_euler_tour();
    test_sparse_table_lca();
    test_virtual_tree();
    test_hld();
    test_diameter();
    test_rerooting();
    test_static_top_tree();
    test_rerooting_static_top_tree();
    test_rerooting_static_top_tree_vertex_component();
    test_centroid_decomposition();
    test_forest();

    long long a = 0, b = 0;
    fast_input >> a >> b;
    fast_output << a + b << '\n';
}
#line 1 "verify/graph/tree/tree_algorithms.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/aplusb"

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



#line 6 "utilities/fast_io.hpp"
#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 7 "verify/graph/tree/tree_algorithms.test.cpp"
#include <numeric>
#include <random>
#include <set>
#line 11 "verify/graph/tree/tree_algorithms.test.cpp"

#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 1 "graph/tree/all.hpp"



#line 1 "graph/tree/cartesian_tree.hpp"



#line 6 "graph/tree/cartesian_tree.hpp"
#include <functional>
#include <limits>
#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"



#include <concepts>

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 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 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 6 "algo/offline/mo.hpp"
#include <cmath>
#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 6 "graph/tree/rerooting_static_top_tree.hpp"
#include <optional>
#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"



#include <bit>
#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 14 "verify/graph/tree/tree_algorithms.test.cpp"

using m1une::graph::Graph;

template <class Hld>
std::vector<int> expand_segments(const Hld& hld, const std::vector<m1une::tree::HldPathSegment>& segments) {
    std::vector<int> result;
    for (auto seg : segments) {
        if (seg.reversed) {
            for (int i = seg.r - 1; i >= seg.l; i--) result.push_back(hld.order[i]);
        } else {
            for (int i = seg.l; i < seg.r; i++) result.push_back(hld.order[i]);
        }
    }
    return result;
}

Graph<long long> sample_tree() {
    Graph<long long> g(7);
    g.add_edge(0, 1, 3);
    g.add_edge(0, 2, 2);
    g.add_edge(1, 3, 4);
    g.add_edge(1, 4, 1);
    g.add_edge(2, 5, 6);
    g.add_edge(5, 6, 2);
    return g;
}

void test_rooted_tree() {
    auto g = sample_tree();
    m1une::tree::RootedTree<long long> tree(g, 0);

    assert(tree.size() == 7);
    assert(!tree.empty());
    assert(tree.root == 0);
    assert(tree.parent[0] == -1);
    assert(tree.parent[3] == 1);
    assert(tree.depth[6] == 3);
    assert(tree.dist[6] == 10);
    assert(tree.subtree_size[0] == 7);
    assert(tree.subtree_size[1] == 3);
    assert(tree.is_ancestor(1, 4));
    assert(!tree.is_ancestor(2, 4));
    assert(tree.in_subtree(4, 1));

    assert(tree.kth_ancestor(6, 0) == 6);
    assert(tree.kth_ancestor(6, 1) == 5);
    assert(tree.kth_ancestor(6, 3) == 0);
    assert(tree.kth_ancestor(6, 4) == -1);
    assert(tree.lca(3, 4) == 1);
    assert(tree.lca(3, 6) == 0);
    assert(tree.dist_edges(3, 6) == 5);
    assert(tree.dist_cost(3, 6) == 17);
    assert(tree.jump(3, 6, 0) == 3);
    assert(tree.jump(3, 6, 1) == 1);
    assert(tree.jump(3, 6, 2) == 0);
    assert(tree.jump(3, 6, 3) == 2);
    assert(tree.jump(3, 6, 5) == 6);
    assert(tree.jump(3, 6, 6) == -1);

    std::vector<int> expected_path = {3, 1, 0, 2, 5, 6};
    assert(tree.path(3, 6) == expected_path);
    std::vector<int> expected_edges = {2, 0, 1, 4, 5};
    assert(tree.path_edges(3, 6) == expected_edges);

    auto [l, r] = tree.subtree_range(1);
    assert(r - l == 3);
    auto sub = tree.subtree_vertices(1);
    std::sort(sub.begin(), sub.end());
    assert((sub == std::vector<int>{1, 3, 4}));
}

void test_euler_tour() {
    auto g = sample_tree();
    m1une::tree::EulerTour<long long> tour(g, 0);

    std::vector<int> expected_order = {0, 1, 3, 4, 2, 5, 6};
    assert(tour.size() == 7);
    assert(tour.visited_size() == 7);
    assert(tour.root == 0);
    assert(tour.order == expected_order);
    assert(tour.parent[6] == 5);
    assert(tour.parent_edge[6] == 5);
    assert(tour.depth[6] == 3);
    assert(tour.dist[6] == 10);
    assert(tour.subtree_size[1] == 3);
    assert(tour.is_ancestor(1, 4));
    assert(!tour.is_ancestor(2, 4));

    auto [l, r] = tour.subtree_range(1);
    assert(l == 1);
    assert(r == 4);
    auto [el, er] = tour.subtree_range(1, true);
    assert(el == 2);
    assert(er == 4);

    std::vector<int> subtree = tour.subtree_vertices(1);
    std::sort(subtree.begin(), subtree.end());
    std::vector<int> expected_subtree = {1, 3, 4};
    assert(subtree == expected_subtree);
}

void test_sparse_table_lca() {
    auto g = sample_tree();
    m1une::tree::RootedTree<long long> tree(g, 0);
    m1une::tree::SparseTableLca<long long> lca(g, 0);

    assert(lca.size() == 7);
    assert(!lca.empty());
    assert(lca.root == 0);
    assert(lca.parent[0] == -1);
    assert(lca.parent[6] == 5);
    assert(lca.depth[6] == 3);
    assert(lca.dist[6] == 10);
    assert(lca.euler.size() == 13);
    assert(lca.first[0] == 0);
    assert(lca.is_ancestor(2, 6));
    assert(!lca.is_ancestor(1, 6));
    assert(lca.in_subtree(6, 2));

    for (int u = 0; u < 7; u++) {
        for (int v = 0; v < 7; v++) {
            assert(lca.lca(u, v) == tree.lca(u, v));
            assert(lca.dist_edges(u, v) == tree.dist_edges(u, v));
            assert(lca.dist_cost(u, v) == tree.dist_cost(u, v));
        }
    }

    auto [l, r] = lca.subtree_range(2);
    assert(r - l == 3);
    std::vector<int> subtree;
    for (int i = l; i < r; i++) subtree.push_back(lca.order[i]);
    std::sort(subtree.begin(), subtree.end());
    assert((subtree == std::vector<int>{2, 5, 6}));
}

void test_virtual_tree() {
    auto graph = sample_tree();
    m1une::tree::VirtualTree<long long> builder(graph, 0);

    auto virtual_tree = builder.build(std::vector<int>{3, 4, 6, 3});
    std::vector<int> expected_vertices = {0, 1, 3, 4, 6};
    assert(virtual_tree.vertex == expected_vertices);
    assert(virtual_tree.parent == std::vector<int>({-1, 0, 1, 1, 0}));
    assert(virtual_tree.parent_edge_count == std::vector<int>({0, 1, 1, 1, 3}));
    assert(virtual_tree.parent_cost == std::vector<long long>({0, 3, 4, 1, 10}));
    assert(virtual_tree.is_key == std::vector<bool>({false, false, true, true, true}));
    assert(virtual_tree.children[0] == std::vector<int>({1, 4}));
    assert(virtual_tree.children[1] == std::vector<int>({2, 3}));
    assert(virtual_tree.root() == 0);
    assert(virtual_tree.root_vertex() == 0);
    assert(virtual_tree.edge_count() == 4);

    auto singleton = builder.build(std::vector<int>{5, 5});
    assert(singleton.size() == 1);
    assert(singleton.vertex[0] == 5);
    assert(singleton.parent[0] == -1);
    assert(singleton.is_key[0]);

    auto empty = builder.build({});
    assert(empty.empty());
    assert(empty.root() == -1);
    assert(empty.root_vertex() == -1);
    assert(empty.edge_count() == 0);

    std::mt19937 random(123456789);
    for (int test = 0; test < 100; test++) {
        int n = 1 + random() % 200;
        Graph<long long> random_graph(n);
        for (int v = 1; v < n; v++) {
            int parent = random() % v;
            long long cost = 1 + random() % 1000000;
            random_graph.add_edge(parent, v, cost);
        }
        m1une::tree::VirtualTree<long long> random_builder(random_graph, 0);
        const auto& lca = random_builder.lca_data();
        for (int query = 0; query < 100; query++) {
            int k = random() % (2 * n + 1);
            std::vector<int> keys(k);
            for (int& v : keys) v = random() % n;
            auto result = random_builder.build(keys);

            std::sort(keys.begin(), keys.end(), [&](int u, int v) {
                return lca.tin[u] < lca.tin[v];
            });
            keys.erase(std::unique(keys.begin(), keys.end()), keys.end());
            std::vector<int> expected = keys;
            for (int i = 1; i < int(keys.size()); i++) expected.push_back(lca.lca(keys[i - 1], keys[i]));
            std::sort(expected.begin(), expected.end(), [&](int u, int v) {
                return lca.tin[u] < lca.tin[v];
            });
            expected.erase(std::unique(expected.begin(), expected.end()), expected.end());
            assert(result.vertex == expected);

            int key_index = 0;
            for (int i = 0; i < result.size(); i++) {
                while (key_index < int(keys.size()) && lca.tin[keys[key_index]] < lca.tin[result.vertex[i]]) {
                    key_index++;
                }
                bool is_key = key_index < int(keys.size()) && keys[key_index] == result.vertex[i];
                assert(result.is_key[i] == is_key);
                if (i == 0) {
                    assert(result.parent[i] == -1);
                    continue;
                }
                int parent = result.parent[i];
                assert(0 <= parent && parent < i);
                assert(lca.is_ancestor(result.vertex[parent], result.vertex[i]));
                assert(result.parent_edge_count[i] == lca.dist_edges(result.vertex[parent], result.vertex[i]));
                assert(result.parent_cost[i] == lca.dist_cost(result.vertex[parent], result.vertex[i]));
                for (int j = parent + 1; j < i; j++) {
                    assert(!lca.is_ancestor(result.vertex[j], result.vertex[i]));
                }
            }
        }
    }
}

void test_hld() {
    auto g = sample_tree();
    m1une::tree::HeavyLightDecomposition<long long> hld(g, 0);

    assert(hld.size() == 7);
    assert(hld.root == 0);
    assert(hld.lca(3, 4) == 1);
    assert(hld.lca(3, 6) == 0);
    assert(hld.dist_edges(3, 6) == 5);
    assert(hld.dist_cost(3, 6) == 17);
    assert(hld.kth_ancestor(6, 2) == 2);
    assert(hld.kth_ancestor(6, 4) == -1);
    assert(hld.jump(3, 6, 4) == 5);

    std::vector<int> expected_path = {3, 1, 0, 2, 5, 6};
    assert(expand_segments(hld, hld.path_segments(3, 6)) == expected_path);
    std::vector<int> expected_edge_vertices = {3, 1, 2, 5, 6};
    assert(expand_segments(hld, hld.path_segments(3, 6, true)) == expected_edge_vertices);

    int segment_count = 0;
    hld.for_each_path(3, 6, [&](int l, int r, bool) {
        assert(l < r);
        segment_count++;
    });
    assert(segment_count == int(hld.path_segments(3, 6).size()));

    auto [vl, vr] = hld.subtree_range(1);
    std::vector<int> subtree;
    for (int i = vl; i < vr; i++) subtree.push_back(hld.order[i]);
    std::sort(subtree.begin(), subtree.end());
    assert((subtree == std::vector<int>{1, 3, 4}));

    auto [el, er] = hld.subtree_range(1, true);
    std::vector<int> edge_subtree;
    for (int i = el; i < er; i++) edge_subtree.push_back(hld.order[i]);
    std::sort(edge_subtree.begin(), edge_subtree.end());
    assert((edge_subtree == std::vector<int>{3, 4}));
}

void test_diameter() {
    auto g = sample_tree();
    auto diameter = m1une::tree::tree_diameter(g);

    assert(!diameter.empty());
    assert(diameter.cost == 17);
    assert(diameter.edge_count == 5);
    assert(diameter.from == diameter.vertices.front());
    assert(diameter.to == diameter.vertices.back());
    std::set<int> endpoints = {diameter.from, diameter.to};
    assert((endpoints == std::set<int>{3, 6}));

    g.erase_edge(1);
    auto split = m1une::tree::tree_diameter(g);
    assert(split.cost == 8);
    assert(split.edge_count == 2);
}

void test_rerooting() {
    auto g = sample_tree();
    auto component_size = m1une::tree::rerooting_dp(
        g,
        0,
        [](int a, int b) { return a + b; },
        [](int acc, int) { return acc + 1; },
        [](int dp, const auto&) { return dp; });
    assert(component_size == std::vector<int>(7, 7));

    auto eccentricity_edges = m1une::tree::rerooting_dp(
        g,
        0,
        [](int a, int b) { return std::max(a, b); },
        [](int acc, int) { return acc; },
        [](int dp, const auto&) { return dp + 1; });
    assert(eccentricity_edges[0] == 3);
    assert(eccentricity_edges[3] == 5);
    assert(eccentricity_edges[6] == 5);

    auto eccentricity_cost = m1une::tree::rerooting_dp(
        g,
        0LL,
        [](long long a, long long b) { return std::max(a, b); },
        [](long long acc, int) { return acc; },
        [](long long dp, const auto& e) { return dp + e.cost; });
    assert(eccentricity_cost[0] == 10);
    assert(eccentricity_cost[3] == 17);
    assert(eccentricity_cost[6] == 17);
}

struct DistancePath {
    long long count;
    long long sum;
    long long length;
};

struct DistancePoint {
    long long count;
    long long sum;
};

struct ColorVertex {
    long long weight;
    int color;
};

struct ColorPath {
    int first_color;
    int last_color;
    long long first_sum;
    long long last_sum;
    bool connected;
};

struct ColorPoint {
    std::array<long long, 2> sum;
};

void test_static_top_tree() {
    auto g = sample_tree();
    std::vector<long long> values = {1, 2, 3, 4, 5, 6, 7};

    auto vertex_sum = m1une::tree::StaticTopTree(
        g,
        values,
        0LL,
        [](long long top, long long bottom, const auto&) {
            return top + bottom;
        },
        [](long long a, long long b) {
            return a + b;
        },
        [](long long path, const auto&) {
            return path;
        },
        [](long long side, long long value, int) {
            return side + value;
        });

    assert(vertex_sum.size() == 7);
    assert(vertex_sum.root() == 0);
    assert(vertex_sum.all_prod() == 28);
    assert(vertex_sum.query() == 28);
    assert(vertex_sum.get(3) == 4);
    assert(vertex_sum.height() > 0);
    vertex_sum.set(3, 100);
    assert(vertex_sum[3] == 100);
    assert(vertex_sum.all_prod() == 124);

    auto root_distance_sum = m1une::tree::StaticTopTree(
        g,
        std::vector<int>(7, 0),
        DistancePoint{0, 0},
        [](DistancePath top, DistancePath bottom, const auto& e) {
            long long shift = top.length + e.cost;
            return DistancePath{top.count + bottom.count, top.sum + bottom.sum + bottom.count * shift,
                                top.length + e.cost + bottom.length};
        },
        [](DistancePoint a, DistancePoint b) {
            return DistancePoint{a.count + b.count, a.sum + b.sum};
        },
        [](DistancePath path, const auto& e) {
            return DistancePoint{path.count, path.sum + path.count * e.cost};
        },
        [](DistancePoint side, int, int) {
            return DistancePath{side.count + 1, side.sum, 0};
        });

    assert(root_distance_sum.all_prod().count == 7);
    assert(root_distance_sum.all_prod().sum == 34);
    root_distance_sum.set_edge_cost(0, 10);
    assert(root_distance_sum.all_prod().sum == 55);
    root_distance_sum.set_edge_cost(1, 10);
    assert(root_distance_sum.all_prod().sum == 79);
}

void test_rerooting_static_top_tree() {
    Graph<long long> g(3);
    int e01 = g.add_edge(0, 1, 2);
    int e12 = g.add_edge(1, 2, 5);
    std::vector<long long> weights = {1, 1, 1};

    auto stt = m1une::tree::RerootingStaticTopTree(
        g,
        weights,
        DistancePoint{0, 0},
        [](DistancePath upper, DistancePath lower, const auto& e) {
            long long shift = upper.length + e.cost;
            return DistancePath{upper.count + lower.count, upper.sum + lower.sum + lower.count * shift,
                                upper.length + e.cost + lower.length};
        },
        [](DistancePath lower, DistancePath upper, const auto& e) {
            long long shift = lower.length + e.cost;
            return DistancePath{lower.count + upper.count, lower.sum + upper.sum + upper.count * shift,
                                lower.length + e.cost + upper.length};
        },
        [](DistancePoint a, DistancePoint b) {
            return DistancePoint{a.count + b.count, a.sum + b.sum};
        },
        [](DistancePath path, const auto& e) {
            return DistancePoint{path.count, path.sum + path.count * e.cost};
        },
        [](DistancePath path, const auto& e) {
            return DistancePoint{path.count, path.sum + path.count * e.cost};
        },
        [](DistancePoint side, long long weight, int) {
            return DistancePath{side.count + weight, side.sum, 0};
        });

    assert(stt.size() == 3);
    assert(stt.root() == 0);
    assert(stt.node_count() >= 3);
    assert(stt.height() > 0);
    assert(stt.all_prod_down().count == 3);
    assert(stt.all_prod_down().sum == 9);
    assert(stt.all_prod_down().length == 7);
    assert(stt.all_prod_up().count == 3);
    assert(stt.all_prod_up().sum == 12);
    assert(stt.all_prod_up().length == 7);

    int root_node = stt.root_node();
    assert(stt.path_down(root_node).sum == stt.all_prod_down().sum);
    assert(stt.path_up(root_node).sum == stt.all_prod_up().sum);
    int one_node = stt.vertex_node(1);
    assert(stt.node(one_node).type == m1une::tree::internal::RerootingStaticTopTreeNodeType::AddVertex);
    assert(stt.parent_node(root_node) == -1);
    assert(stt.point_id().count == 0);
    assert(stt.local_point_node(1) == -1);
    assert(stt.local_point(1).count == 0);

    using RerootingStepType = decltype(stt)::step_type;
    using RerootingNodeType = decltype(stt)::node_type;
    auto steps = stt.rerooting_steps(1);
    std::vector<decltype(stt)::RerootingStep> streamed_steps;
    stt.for_each_rerooting_step(1, [&](const auto& step) {
        streamed_steps.push_back(step);
    });
    assert(streamed_steps.size() == steps.size());
    int cur = one_node;
    for (int i = 0; i < int(steps.size()); i++) {
        const auto& step = steps[i];
        const auto& streamed = streamed_steps[i];
        assert(streamed.type == step.type);
        assert(streamed.node == step.node);
        assert(streamed.sibling == step.sibling);
        assert(streamed.vertex == step.vertex);
        assert(streamed.edge.from == step.edge.from);
        assert(streamed.edge.to == step.edge.to);
        assert(streamed.edge.id == step.edge.id);

        assert(stt.parent_node(cur) == step.node);
        const auto& parent = stt.node(step.node);
        if (step.type == RerootingStepType::CompressLower) {
            assert(parent.type == RerootingNodeType::Compress);
            assert(parent.left == cur);
            assert(parent.right == step.sibling);
            assert(stt.node(step.sibling).path_down.has_value());
        } else if (step.type == RerootingStepType::CompressUpper) {
            assert(parent.type == RerootingNodeType::Compress);
            assert(parent.right == cur);
            assert(parent.left == step.sibling);
            assert(stt.node(step.sibling).path_up.has_value());
        } else if (step.type == RerootingStepType::RakeLeft) {
            assert(parent.type == RerootingNodeType::Rake);
            assert(parent.right == cur);
            assert(parent.left == step.sibling);
            assert(stt.node(step.sibling).point.has_value());
        } else if (step.type == RerootingStepType::RakeRight) {
            assert(parent.type == RerootingNodeType::Rake);
            assert(parent.left == cur);
            assert(parent.right == step.sibling);
            assert(stt.node(step.sibling).point.has_value());
        } else if (step.type == RerootingStepType::AddEdge) {
            assert(parent.type == RerootingNodeType::AddEdge);
            assert(parent.left == cur);
        } else {
            assert(step.type == RerootingStepType::AddVertex);
            assert(parent.type == RerootingNodeType::AddVertex);
            assert(parent.left == cur);
            assert(parent.vertex == step.vertex);
        }
        cur = step.node;
    }
    assert(cur == stt.root_node());

    auto edge = m1une::graph::Edge<long long>(0, 1, 2, e01);
    auto reversed = decltype(stt)::reverse_edge(edge);
    assert(reversed.from == 1);
    assert(reversed.to == 0);
    DistancePath one = stt.add_vertex(stt.point_id(), 1LL, 0);
    assert(one.count == 1);
    assert(one.sum == 0);
    auto down_point = stt.add_edge_down(one, edge);
    auto up_point = stt.add_edge_up(one, reversed);
    assert(down_point.sum == 2);
    assert(up_point.sum == 2);
    auto raked = stt.rake(down_point, up_point);
    assert(raked.count == 2);
    assert(raked.sum == 4);
    assert(stt.compress_down(one, one, edge).sum == 2);
    assert(stt.compress_up(one, one, reversed).sum == 2);

    stt.set_edge_cost(e01, 10);
    assert(stt.all_prod_down().count == 3);
    assert(stt.all_prod_down().sum == 25);
    assert(stt.all_prod_down().length == 15);
    assert(stt.all_prod_up().sum == 20);
    assert(stt.all_prod_up().length == 15);

    stt.set(0, 3);
    assert(stt[0] == 3);
    assert(stt.all_prod_down().count == 5);
    assert(stt.all_prod_down().sum == 25);
    assert(stt.all_prod_up().count == 5);
    assert(stt.all_prod_up().sum == 50);

    stt.set_edge_cost(e12, 1);
    assert(stt.all_prod_down().sum == 21);
    assert(stt.all_prod_up().sum == 34);
}

void test_rerooting_static_top_tree_vertex_component() {
    auto g = sample_tree();
    std::vector<ColorVertex> values = {
        ColorVertex{1, 0},      ColorVertex{10, 0},     ColorVertex{100, 1},    ColorVertex{1000, 0},
        ColorVertex{10000, 1},  ColorVertex{100000, 1}, ColorVertex{1000000, 1},
    };

    auto compress = [](ColorPath a, ColorPath b, const auto&) {
        bool join = a.last_color == b.first_color;
        ColorPath res{a.first_color, b.last_color, a.first_sum, b.last_sum, false};
        if (join && a.connected) res.first_sum += b.first_sum;
        if (join && b.connected) res.last_sum += a.last_sum;
        res.connected = a.connected && b.connected && join;
        return res;
    };
    auto rake = [](ColorPoint a, ColorPoint b) {
        return ColorPoint{a.sum[0] + b.sum[0], a.sum[1] + b.sum[1]};
    };
    auto add_edge = [](ColorPath path, const auto&) {
        ColorPoint res{};
        res.sum[path.first_color] = path.first_sum;
        return res;
    };
    auto add_vertex = [](ColorPoint side, ColorVertex value, int) {
        long long sum = value.weight + side.sum[value.color];
        return ColorPath{value.color, value.color, sum, sum, true};
    };

    auto stt = m1une::tree::RerootingStaticTopTree(
        g, values, ColorPoint{0, 0}, compress, compress, rake, add_edge, add_edge, add_vertex);

    using ColorStt = decltype(stt);
    struct QueryFolder {
        const ColorStt& stt;
        const std::vector<ColorVertex>& values;
        int color = 0;
        long long answer = 0;
        bool touches_top = false;
        bool touches_bottom = false;
        bool pending_open = false;
        ColorPoint pending{};

        void start(int v, const ColorVertex& value, const ColorPoint& local) {
            color = value.color;
            answer = value.weight + local.sum[color];
            touches_top = true;
            touches_bottom = true;
            pending_open = false;
            pending = stt.point_id();
            assert(values[v].color == color);
        }

        void compress_lower(const ColorPath& lower, ColorStt::edge_type) {
            bool connect = touches_bottom && lower.first_color == color;
            if (connect) answer += lower.first_sum;
            touches_bottom = connect && lower.connected;
        }

        void compress_upper(const ColorPath& upper, ColorStt::edge_type) {
            bool connect = touches_top && upper.first_color == color;
            if (connect) answer += upper.first_sum;
            touches_top = connect && upper.connected;
        }

        void add_edge(ColorStt::edge_type) {
            pending_open = touches_top;
            pending = stt.point_id();
        }

        void rake_left(const ColorPoint& point) {
            if (pending_open) pending = stt.rake(point, pending);
        }

        void rake_right(const ColorPoint& point) {
            if (pending_open) pending = stt.rake(pending, point);
        }

        void add_vertex(int, const ColorVertex& value) {
            if (pending_open && value.color == color) {
                answer += value.weight + pending.sum[color];
                touches_top = true;
                touches_bottom = true;
            } else {
                touches_top = false;
                touches_bottom = false;
            }
            pending_open = false;
            pending = stt.point_id();
        }

        long long result() const {
            return answer;
        }
    };

    auto query = [&](int v) {
        return stt.fold_rerooting(v, QueryFolder{stt, values});
    };

    auto brute = [&](int start) {
        int color = values[start].color;
        long long answer = 0;
        std::vector<char> seen(g.size(), false);
        std::vector<int> stack = {start};
        seen[start] = true;
        while (!stack.empty()) {
            int v = stack.back();
            stack.pop_back();
            answer += values[v].weight;
            for (const auto& e : g[v]) {
                if (seen[e.to] || values[e.to].color != color) continue;
                seen[e.to] = true;
                stack.push_back(e.to);
            }
        }
        return answer;
    };

    auto check_all = [&]() {
        for (int v = 0; v < g.size(); v++) assert(query(v) == brute(v));
    };

    check_all();
    values[2].color ^= 1;
    stt.set(2, values[2]);
    check_all();
    values[5].weight += 7;
    stt.set(5, values[5]);
    check_all();
    values[1].color ^= 1;
    stt.set(1, values[1]);
    check_all();
    values[4].weight += 11;
    stt.set(4, values[4]);
    check_all();
}

void test_centroid_decomposition() {
    auto g = sample_tree();
    m1une::tree::CentroidDecomposition<long long> cd(g);

    assert(cd.size() == 7);
    assert(!cd.empty());
    assert(cd.root() == 0);
    assert(cd.roots == std::vector<int>{0});
    assert(cd.parent[cd.root()] == -1);
    assert(cd.depth[cd.root()] == 0);
    assert(cd.order.size() == 7);

    int child_count = 0;
    for (const auto& ch : cd.children) child_count += int(ch.size());
    assert(child_count == 6);
    for (int v = 0; v < 7; v++) {
        if (v == cd.root()) continue;
        assert(cd.parent[v] != -1);
        assert(cd.depth[v] == cd.depth[cd.parent[v]] + 1);
    }
}

void test_forest() {
    Graph<int> g(4);
    g.add_edge(0, 1, 5);
    g.add_edge(2, 3, 7);

    auto diameter = m1une::tree::tree_diameter(g);
    assert(diameter.cost == 7);
    assert(diameter.edge_count == 1);

    auto component_size = m1une::tree::rerooting_dp(
        g,
        0,
        [](int a, int b) { return a + b; },
        [](int acc, int) { return acc + 1; },
        [](int dp, const auto&) { return dp; });
    assert(component_size == std::vector<int>(4, 2));

    m1une::tree::CentroidDecomposition<int> cd(g);
    assert(cd.roots.size() == 2);
    assert(cd.order.size() == 4);
}

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

    test_rooted_tree();
    test_euler_tour();
    test_sparse_table_lca();
    test_virtual_tree();
    test_hld();
    test_diameter();
    test_rerooting();
    test_static_top_tree();
    test_rerooting_static_top_tree();
    test_rerooting_static_top_tree_vertex_component();
    test_centroid_decomposition();
    test_forest();

    long long a = 0, b = 0;
    fast_input >> a >> b;
    fast_output << a + b << '\n';
}
Back to top page