< sigさんのAI生成コード置き場

雑多(未確認)

投稿者sig
言語PyPy3
更新日時 (JST)2026-10-17 05:54:34
サイズ36,813 Byte
参照URL

日時はサーバーが記録したものです。更新すると更新日時が変わるので、不都合がある場合は新しく投稿してください。

テキストで開く
"""Segment Tree Beats for Python.

Supports the following operations on an integer (or comparable numeric) array:

    range_chmin(l, r, x)  : A[i] = min(A[i], x) for l <= i < r
    range_chmax(l, r, x)  : A[i] = max(A[i], x) for l <= i < r
    range_add(l, r, x)    : A[i] += x for l <= i < r
    range_set(l, r, x)    : A[i] = x for l <= i < r
    range_sum(l, r)       : sum(A[l:r])
    range_max(l, r)       : max(A[l:r])
    range_min(l, r)       : min(A[l:r])

All ranges are half-open: [l, r), as in Python slices and AtCoder Library.

Notes
-----
* Empty updates and range_sum(l, l) are allowed.
* range_min / range_max require a non-empty range and raise ValueError otherwise.
* This is a Segment Tree Beats implementation, not a plain lazy segment tree.
  The chmin/chmax operations use the second maximum/minimum and their counts to
  decide whether a whole node can be updated without descending to its children.
* Updates are amortized, not guaranteed O(log N) in the worst case per call.

Example
-------
    seg = SegmentTreeBeats([2, 5, 7, 9, 12])
    seg.range_chmin(1, 5, 8)  # [2, 5, 7, 8, 8]
    seg.range_add(0, 2, 3)   # [5, 8, 7, 8, 8]
    assert seg.range_sum(0, 5) == 36
    assert seg.range_max(0, 5) == 8
    assert seg.to_list() == [5, 8, 7, 8, 8]
"""

from __future__ import annotations

from math import inf
from typing import Iterable, List, Optional, TypeVar

T = TypeVar("T", int, float)


class SegmentTreeBeats:
    """Range chmin/chmax/add/set and range sum/min/max queries."""

    __slots__ = (
        "n", "_length", "_sum", "_max1", "_max2", "_max_count",
        "_min1", "_min2", "_min_count", "_lazy_add", "_lazy_set",
    )

    def __init__(self, values: Iterable[T]):
        a = list(values)
        self.n = len(a)
        size = max(1, 4 * self.n)

        self._length: List[int] = [0] * size
        self._sum: List[T] = [0] * size  # type: ignore[list-item]
        self._max1 = [-inf] * size
        self._max2 = [-inf] * size
        self._max_count = [0] * size
        self._min1 = [inf] * size
        self._min2 = [inf] * size
        self._min_count = [0] * size
        self._lazy_add: List[T] = [0] * size  # type: ignore[list-item]
        self._lazy_set: List[Optional[T]] = [None] * size

        if self.n:
            self._build(1, 0, self.n, a)

    # ---------- Construction and aggregate maintenance ----------

    def _build(self, k: int, left: int, right: int, a: List[T]) -> None:
        self._length[k] = right - left
        if right - left == 1:
            v = a[left]
            self._sum[k] = v
            self._max1[k] = self._min1[k] = v
            self._max2[k] = -inf
            self._min2[k] = inf
            self._max_count[k] = self._min_count[k] = 1
            return
        mid = (left + right) // 2
        self._build(k * 2, left, mid, a)
        self._build(k * 2 + 1, mid, right, a)
        self._pull(k)

    def _pull(self, k: int) -> None:
        lc, rc = k * 2, k * 2 + 1
        self._sum[k] = self._sum[lc] + self._sum[rc]

        lmax, rmax = self._max1[lc], self._max1[rc]
        if lmax > rmax:
            self._max1[k] = lmax
            self._max_count[k] = self._max_count[lc]
            self._max2[k] = max(self._max2[lc], rmax)
        elif lmax < rmax:
            self._max1[k] = rmax
            self._max_count[k] = self._max_count[rc]
            self._max2[k] = max(lmax, self._max2[rc])
        else:
            self._max1[k] = lmax
            self._max_count[k] = self._max_count[lc] + self._max_count[rc]
            self._max2[k] = max(self._max2[lc], self._max2[rc])

        lmin, rmin = self._min1[lc], self._min1[rc]
        if lmin < rmin:
            self._min1[k] = lmin
            self._min_count[k] = self._min_count[lc]
            self._min2[k] = min(self._min2[lc], rmin)
        elif lmin > rmin:
            self._min1[k] = rmin
            self._min_count[k] = self._min_count[rc]
            self._min2[k] = min(lmin, self._min2[rc])
        else:
            self._min1[k] = lmin
            self._min_count[k] = self._min_count[lc] + self._min_count[rc]
            self._min2[k] = min(self._min2[lc], self._min2[rc])

    # ---------- Applying operations directly to a node ----------

    def _apply_set(self, k: int, x: T) -> None:
        length = self._length[k]
        self._sum[k] = x * length
        self._max1[k] = self._min1[k] = x
        self._max2[k] = -inf
        self._min2[k] = inf
        self._max_count[k] = self._min_count[k] = length
        self._lazy_set[k] = x
        self._lazy_add[k] = 0  # type: ignore[assignment]

    def _apply_add(self, k: int, x: T) -> None:
        self._sum[k] += x * self._length[k]
        self._max1[k] += x
        self._min1[k] += x
        # +/- infinity sentinels remain +/- infinity after addition.
        self._max2[k] += x
        self._min2[k] += x
        if self._lazy_set[k] is not None:
            self._lazy_set[k] += x  # type: ignore[operator]
        else:
            self._lazy_add[k] += x

    def _apply_chmin(self, k: int, x: T) -> None:
        """Apply chmin when max2 < x < max1 (or max1 <= x)."""
        old_max = self._max1[k]
        if old_max <= x:
            return
        # Caller/push guarantees that the second maximum is strictly below x.
        if not self._max2[k] < x:
            raise AssertionError("invalid Beats chmin application: max2 >= x")
        self._sum[k] -= (old_max - x) * self._max_count[k]
        if self._min1[k] == old_max:
            self._min1[k] = x
        elif self._min2[k] == old_max:
            self._min2[k] = x
        self._max1[k] = x

    def _apply_chmax(self, k: int, x: T) -> None:
        """Apply chmax when min1 < x < min2 (or min1 >= x)."""
        old_min = self._min1[k]
        if old_min >= x:
            return
        # Caller/push guarantees that the second minimum is strictly above x.
        if not x < self._min2[k]:
            raise AssertionError("invalid Beats chmax application: min2 <= x")
        self._sum[k] += (x - old_min) * self._min_count[k]
        if self._max1[k] == old_min:
            self._max1[k] = x
        elif self._max2[k] == old_min:
            self._max2[k] = x
        self._min1[k] = x

    # ---------- Lazy propagation ----------

    def _push(self, k: int) -> None:
        if self._length[k] <= 1:
            return
        lc, rc = k * 2, k * 2 + 1

        value = self._lazy_set[k]
        if value is not None:
            self._apply_set(lc, value)
            self._apply_set(rc, value)
            self._lazy_set[k] = None

        add = self._lazy_add[k]
        if add != 0:
            self._apply_add(lc, add)
            self._apply_add(rc, add)
            self._lazy_add[k] = 0  # type: ignore[assignment]

        # Propagate the parent's implicit upper/lower bounds to each child.
        parent_max = self._max1[k]
        parent_min = self._min1[k]
        if self._max1[lc] > parent_max:
            self._apply_chmin(lc, parent_max)
        if self._min1[lc] < parent_min:
            self._apply_chmax(lc, parent_min)
        if self._max1[rc] > parent_max:
            self._apply_chmin(rc, parent_max)
        if self._min1[rc] < parent_min:
            self._apply_chmax(rc, parent_min)

    # ---------- Range updates ----------

    def range_chmin(self, l: int, r: int, x: T) -> None:
        """Set every A[i] in [l, r) to min(A[i], x)."""
        self._check_range(l, r)
        if l < r and self.n:
            self._range_chmin(1, 0, self.n, l, r, x)

    def _range_chmin(self, k: int, nl: int, nr: int, ql: int, qr: int, x: T) -> None:
        if qr <= nl or nr <= ql or self._max1[k] <= x:
            return
        if ql <= nl and nr <= qr and self._max2[k] < x:
            self._apply_chmin(k, x)
            return
        self._push(k)
        mid = (nl + nr) // 2
        self._range_chmin(k * 2, nl, mid, ql, qr, x)
        self._range_chmin(k * 2 + 1, mid, nr, ql, qr, x)
        self._pull(k)

    def range_chmax(self, l: int, r: int, x: T) -> None:
        """Set every A[i] in [l, r) to max(A[i], x)."""
        self._check_range(l, r)
        if l < r and self.n:
            self._range_chmax(1, 0, self.n, l, r, x)

    def _range_chmax(self, k: int, nl: int, nr: int, ql: int, qr: int, x: T) -> None:
        if qr <= nl or nr <= ql or self._min1[k] >= x:
            return
        if ql <= nl and nr <= qr and x < self._min2[k]:
            self._apply_chmax(k, x)
            return
        self._push(k)
        mid = (nl + nr) // 2
        self._range_chmax(k * 2, nl, mid, ql, qr, x)
        self._range_chmax(k * 2 + 1, mid, nr, ql, qr, x)
        self._pull(k)

    def range_add(self, l: int, r: int, x: T) -> None:
        """Add x to every A[i] in [l, r)."""
        self._check_range(l, r)
        if l < r and self.n:
            self._range_add(1, 0, self.n, l, r, x)

    def _range_add(self, k: int, nl: int, nr: int, ql: int, qr: int, x: T) -> None:
        if qr <= nl or nr <= ql:
            return
        if ql <= nl and nr <= qr:
            self._apply_add(k, x)
            return
        self._push(k)
        mid = (nl + nr) // 2
        self._range_add(k * 2, nl, mid, ql, qr, x)
        self._range_add(k * 2 + 1, mid, nr, ql, qr, x)
        self._pull(k)

    def range_set(self, l: int, r: int, x: T) -> None:
        """Assign x to every A[i] in [l, r)."""
        self._check_range(l, r)
        if l < r and self.n:
            self._range_set(1, 0, self.n, l, r, x)

    def _range_set(self, k: int, nl: int, nr: int, ql: int, qr: int, x: T) -> None:
        if qr <= nl or nr <= ql:
            return
        if ql <= nl and nr <= qr:
            self._apply_set(k, x)
            return
        self._push(k)
        mid = (nl + nr) // 2
        self._range_set(k * 2, nl, mid, ql, qr, x)
        self._range_set(k * 2 + 1, mid, nr, ql, qr, x)
        self._pull(k)

    # ---------- Range queries ----------

    def range_sum(self, l: int, r: int):
        """Return sum(A[l:r]); an empty range has sum 0."""
        self._check_range(l, r)
        if l == r or self.n == 0:
            return 0
        return self._range_sum(1, 0, self.n, l, r)

    def _range_sum(self, k: int, nl: int, nr: int, ql: int, qr: int):
        if qr <= nl or nr <= ql:
            return 0
        if ql <= nl and nr <= qr:
            return self._sum[k]
        self._push(k)
        mid = (nl + nr) // 2
        return self._range_sum(k * 2, nl, mid, ql, qr) + self._range_sum(
            k * 2 + 1, mid, nr, ql, qr
        )

    def range_max(self, l: int, r: int):
        """Return max(A[l:r]); raises ValueError for an empty range."""
        self._check_range(l, r)
        if l == r:
            raise ValueError("range_max requires a non-empty range")
        return self._range_max(1, 0, self.n, l, r)

    def _range_max(self, k: int, nl: int, nr: int, ql: int, qr: int):
        if qr <= nl or nr <= ql:
            return -inf
        if ql <= nl and nr <= qr:
            return self._max1[k]
        self._push(k)
        mid = (nl + nr) // 2
        return max(
            self._range_max(k * 2, nl, mid, ql, qr),
            self._range_max(k * 2 + 1, mid, nr, ql, qr),
        )

    def range_min(self, l: int, r: int):
        """Return min(A[l:r]); raises ValueError for an empty range."""
        self._check_range(l, r)
        if l == r:
            raise ValueError("range_min requires a non-empty range")
        return self._range_min(1, 0, self.n, l, r)

    def _range_min(self, k: int, nl: int, nr: int, ql: int, qr: int):
        if qr <= nl or nr <= ql:
            return inf
        if ql <= nl and nr <= qr:
            return self._min1[k]
        self._push(k)
        mid = (nl + nr) // 2
        return min(
            self._range_min(k * 2, nl, mid, ql, qr),
            self._range_min(k * 2 + 1, mid, nr, ql, qr),
        )

    # ---------- Convenience methods ----------

    def all_sum(self):
        """Return the sum of the whole array (0 for an empty tree)."""
        return self._sum[1] if self.n else 0

    def all_max(self):
        """Return the maximum of the whole array; raises ValueError if empty."""
        if not self.n:
            raise ValueError("all_max requires a non-empty tree")
        return self._max1[1]

    def all_min(self):
        """Return the minimum of the whole array; raises ValueError if empty."""
        if not self.n:
            raise ValueError("all_min requires a non-empty tree")
        return self._min1[1]

    def __getitem__(self, i: int):
        """Return A[i]."""
        if not 0 <= i < self.n:
            raise IndexError("index out of range")
        return self.range_max(i, i + 1)

    def __len__(self) -> int:
        return self.n

    def to_list(self) -> List:
        """Materialize and return the current array in O(N) time."""
        if not self.n:
            return []
        result = [None] * self.n

        def dfs(k: int, left: int, right: int) -> None:
            if right - left == 1:
                result[left] = self._max1[k]
                return
            self._push(k)
            mid = (left + right) // 2
            dfs(k * 2, left, mid)
            dfs(k * 2 + 1, mid, right)

        dfs(1, 0, self.n)
        return result

    def _check_range(self, l: int, r: int) -> None:
        if not (0 <= l <= r <= self.n):
            raise ValueError(f"invalid range [{l}, {r}) for n={self.n}")


if __name__ == "__main__":
    # Small smoke test. Run `python segment_tree_beats.py` to check the module.
    seg = SegmentTreeBeats([2, 5, 7, 9, 12])
    seg.range_chmin(1, 5, 8)
    assert seg.to_list() == [2, 5, 7, 8, 8]
    seg.range_add(0, 2, 3)
    assert seg.to_list() == [5, 8, 7, 8, 8]
    assert seg.range_sum(0, 5) == 36
    assert seg.range_max(0, 5) == 8
    assert seg.range_min(0, 5) == 5
    seg.range_chmax(0, 4, 7)
    assert seg.to_list() == [7, 8, 7, 8, 8]
    seg.range_set(1, 4, -2)
    assert seg.to_list() == [7, -2, -2, -2, 8]
    print("SegmentTreeBeats smoke test: OK")

"""Wavelet Matrix for static range-frequency / order-statistics queries.

Values may be arbitrary comparable integers; values are coordinate-compressed.
Intervals are half-open [l, r). Complexity: O(log sigma) per query.
"""
from bisect import bisect_left
from array import array

class WaveletMatrix:
    def __init__(self, data):
        self.n = len(data)
        self.values = sorted(set(data))
        sigma = len(self.values)
        self.bitlen = max(1, (sigma - 1).bit_length())
        rank = {v: i for i, v in enumerate(self.values)}
        cur = [rank[x] for x in data]
        self.pref_ones = []
        self.zeros = []
        for bit in range(self.bitlen - 1, -1, -1):
            pref = array('I', [0])
            zero, one = [], []
            acc = 0
            for x in cur:
                if (x >> bit) & 1:
                    acc += 1
                    one.append(x)
                else:
                    zero.append(x)
                pref.append(acc)
            self.pref_ones.append(pref)
            self.zeros.append(len(zero))
            cur = zero + one

    def _check_range(self, l, r):
        if not (0 <= l <= r <= self.n):
            raise IndexError("expected 0 <= l <= r <= n")

    def _count_rank_lt(self, l, r, k):
        """Count compressed ranks < k in [l,r)."""
        if k <= 0 or l == r:
            return 0
        if k >= len(self.values):
            return r - l
        result = 0
        for level, bit in enumerate(range(self.bitlen - 1, -1, -1)):
            pref = self.pref_ones[level]
            ones_l, ones_r = pref[l], pref[r]
            zeros_l, zeros_r = l - ones_l, r - ones_r
            if (k >> bit) & 1:
                result += zeros_r - zeros_l
                l = self.zeros[level] + ones_l
                r = self.zeros[level] + ones_r
            else:
                l, r = zeros_l, zeros_r
        return result

    def range_freq_lt(self, l, r, x):
        """Count i in [l,r) such that data[i] < x."""
        self._check_range(l, r)
        return self._count_rank_lt(l, r, bisect_left(self.values, x))

    def range_freq(self, l, r, lower, upper):
        """Count i in [l,r) such that lower <= data[i] < upper."""
        self._check_range(l, r)
        if lower >= upper:
            return 0
        return self.range_freq_lt(l, r, upper) - self.range_freq_lt(l, r, lower)

    def kth_smallest(self, l, r, k):
        """Return the 0-indexed k-th smallest value in data[l:r]."""
        self._check_range(l, r)
        if not (0 <= k < r - l):
            raise IndexError("k must satisfy 0 <= k < r-l")
        rank = 0
        for level, bit in enumerate(range(self.bitlen - 1, -1, -1)):
            pref = self.pref_ones[level]
            ones_l, ones_r = pref[l], pref[r]
            zeros_l, zeros_r = l - ones_l, r - ones_r
            zero_count = zeros_r - zeros_l
            rank <<= 1
            if k < zero_count:
                l, r = zeros_l, zeros_r
            else:
                k -= zero_count
                rank |= 1
                l = self.zeros[level] + ones_l
                r = self.zeros[level] + ones_r
        return self.values[rank]

    def range_min(self, l, r):
        return self.kth_smallest(l, r, 0)

    def range_max(self, l, r):
        return self.kth_smallest(l, r, r - l - 1)

"""Slope Trick for a convex piecewise-linear function.

The represented function is maintained up to additive constants as a sum of
hinges. Supports adding max(a-x, 0), max(x-a, 0), |x-a|, constants, and a
horizontal translation. `get_min()` returns the minimum function value;
`get_argmin()` returns the interval of minimizers.

Heaps maintain the invariant max(L) <= min(R). L is a max-heap (negated), R a
min-heap. Offsets make `shift(dx)` O(1).
"""
from heapq import heappush, heappop

class SlopeTrick:
    def __init__(self):
        self.L = []
        self.R = []
        self.add_l = 0
        self.add_r = 0
        self.min_f = 0

    def _left_max(self):
        return -self.L[0] + self.add_l

    def _right_min(self):
        return self.R[0] + self.add_r

    def _rebalance(self):
        while self.L and self.R and self._left_max() > self._right_min():
            x = self._left_max()
            y = self._right_min()
            self.min_f += x - y
            heappop(self.L)
            heappop(self.R)
            heappush(self.L, -(y - self.add_l))
            heappush(self.R, x - self.add_r)

    def add_const(self, c):
        self.min_f += c

    def add_a_minus_x(self, a):
        """Add max(a-x, 0) to the function."""
        heappush(self.L, -(a - self.add_l))
        self._rebalance()

    def add_x_minus_a(self, a):
        """Add max(x-a, 0) to the function."""
        heappush(self.R, a - self.add_r)
        self._rebalance()

    def add_abs(self, a):
        """Add |x-a| to the function."""
        self.add_a_minus_x(a)
        self.add_x_minus_a(a)

    def shift(self, dx):
        """Horizontal translation: new_f(x) = old_f(x-dx)."""
        self.add_l += dx
        self.add_r += dx

    def get_min(self):
        return self.min_f

    def get_argmin(self):
        """Return (leftmost minimizer, rightmost minimizer); may be infinite."""
        left = self._left_max() if self.L else float('-inf')
        right = self._right_min() if self.R else float('inf')
        return left, right

"""Generic all-roots tree DP (rerooting).

`graph[u]` contains `(v, edge_data)` for every directed adjacency of the tree.
`merge(a, b)` combines two neighbor contributions (must be associative).
`vertex(merged_value, u)` adds/handles vertex u.
`edge(value_at_u, u, v, edge_data)` converts the message from u's side into
its contribution as seen from v. Identity is the neutral element for merge.

Returns `answer[u]`, the DP result when u is considered the root.
"""

def rerooting(n, graph, identity, merge, vertex, edge):
    if n == 0:
        return []
    parent = [-1] * n
    parent_edge = [None] * n
    order = [0]
    parent[0] = 0
    for u in order:
        for v, data in graph[u]:
            if v == parent[u]:
                continue
            if parent[v] != -1:
                continue
            parent[v] = u
            parent_edge[v] = data
            order.append(v)
    if len(order) != n:
        raise ValueError("graph must be a connected tree")

    # down[u]: result for the subtree rooted at u, excluding parent side.
    down = [None] * n
    for u in reversed(order):
        agg = identity
        for v, data in graph[u]:
            if parent[v] == u:
                agg = merge(agg, edge(down[v], v, u, data))
        down[u] = vertex(agg, u)

    # up[u] is the message from parent[u] to u (already converted across edge).
    up = [None] * n
    answer = [None] * n
    for u in order:
        contributions = []
        for v, data in graph[u]:
            if v == parent[u] and u != 0:
                contributions.append(up[u])
            else:
                contributions.append(edge(down[v], v, u, data))
        m = len(contributions)
        pref = [identity] * (m + 1)
        suff = [identity] * (m + 1)
        for i in range(m):
            pref[i + 1] = merge(pref[i], contributions[i])
        for i in range(m - 1, -1, -1):
            suff[i] = merge(contributions[i], suff[i + 1])
        answer[u] = vertex(pref[m], u)

        for i, (v, data) in enumerate(graph[u]):
            if parent[v] == u:
                without_v = merge(pref[i], suff[i + 1])
                up[v] = edge(vertex(without_v, u), u, v, data)
    return answer
"""Classic Mo's algorithm for offline range queries [l, r).

Example:
    mo = Mo(n, queries)  # queries is [(l, r), ...]
    answers = mo.run(add, remove, get_answer)

`add(i)` / `remove(i)` mutate the maintained window state. `get_answer()`
returns the answer for the current window. Queries are returned in input order.
"""
from math import isqrt

class Mo:
    def __init__(self, n, queries, block_size=None):
        self.n = n
        self.queries = [(l, r, i) for i, (l, r) in enumerate(queries)]
        if any(not (0 <= l <= r <= n) for l, r, _ in self.queries):
            raise ValueError("queries must satisfy 0 <= l <= r <= n")
        q = len(queries)
        if block_size is None:
            block_size = max(1, n // max(1, isqrt(max(1, q))))
        self.block_size = max(1, block_size)
        b = self.block_size
        self.ordered = sorted(
            self.queries,
            key=lambda x: (x[0] // b, x[1] if (x[0] // b) % 2 == 0 else -x[1]),
        )

    def run(self, add, remove, get_answer):
        ans = [None] * len(self.queries)
        left = right = 0
        for l, r, qi in self.ordered:
            while left > l:
                left -= 1
                add(left)
            while right < r:
                add(right)
                right += 1
            while left < l:
                remove(left)
                left += 1
            while right > r:
                right -= 1
                remove(right)
            ans[qi] = get_answer()
        return ans

"""Lowlink for an undirected graph: bridges and articulation points.

Input edges is a list [(u,v), ...]. Parallel edges are supported because DFS
tracks edge IDs, not just parent vertices. Vertices are 0-indexed.
"""
import sys

class LowLink:
    def __init__(self, n, edges):
        self.n = n
        self.edges = edges
        self.g = [[] for _ in range(n)]
        for eid, (u, v) in enumerate(edges):
            self.g[u].append((v, eid))
            self.g[v].append((u, eid))
        self.timer = 0
        self.tin = [-1] * n
        self.low = [-1] * n
        self.bridges = []       # edge IDs
        self.articulation = []  # vertex IDs
        self._art = [False] * n
        sys.setrecursionlimit(max(1_000_000, n * 2 + 10))
        for v in range(n):
            if self.tin[v] == -1:
                self._dfs(v, -1)
        self.bridges.sort()
        self.articulation = [v for v, yes in enumerate(self._art) if yes]

    def _dfs(self, v, parent_eid):
        self.tin[v] = self.low[v] = self.timer
        self.timer += 1
        children = 0
        for to, eid in self.g[v]:
            if eid == parent_eid:
                continue
            if self.tin[to] != -1:
                self.low[v] = min(self.low[v], self.tin[to])
            else:
                self._dfs(to, eid)
                self.low[v] = min(self.low[v], self.low[to])
                if self.low[to] > self.tin[v]:
                    self.bridges.append(eid)
                if parent_eid != -1 and self.low[to] >= self.tin[v]:
                    self._art[v] = True
                children += 1
        if parent_eid == -1 and children > 1:
            self._art[v] = True

    def bridge_edges(self):
        return [self.edges[eid] for eid in self.bridges]

"""Link-Cut Tree for a dynamic forest with vertex values.

Supports link/cut/connectivity, point assignment, and path-sum queries.
Vertices are 0-indexed. `link(u,v)` / `cut(u,v)` return False if the operation
would be invalid (cycle / non-edge respectively).
"""

class LinkCutTree:
    def __init__(self, values):
        self.n = len(values)
        self.ch = [[-1, -1] for _ in values]
        self.fa = [-1] * self.n
        self.rev = [False] * self.n
        self.val = list(values)
        self.sm = list(values)
        self.mx = list(values)
        self.mn = list(values)

    def _is_aux_root(self, x):
        p = self.fa[x]
        return p == -1 or (self.ch[p][0] != x and self.ch[p][1] != x)

    def _apply_rev(self, x):
        if x != -1:
            self.ch[x][0], self.ch[x][1] = self.ch[x][1], self.ch[x][0]
            self.rev[x] = not self.rev[x]

    def _push(self, x):
        if self.rev[x]:
            self._apply_rev(self.ch[x][0])
            self._apply_rev(self.ch[x][1])
            self.rev[x] = False

    def _pull(self, x):
        self.sm[x] = self.val[x]
        self.mx[x] = self.val[x]
        self.mn[x] = self.val[x]
        left, right = self.ch[x]
        if left != -1:
            self.sm[x] += self.sm[left]
            self.mx[x] = max(self.mx[x], self.mx[left])
            self.mn[x] = min(self.mn[x], self.mn[left])
        if right != -1:
            self.sm[x] += self.sm[right]
            self.mx[x] = max(self.mx[x], self.mx[right])
            self.mn[x] = min(self.mn[x], self.mn[right])

    def _rotate(self, x):
        p = self.fa[x]
        g = self.fa[p]
        d = 1 if self.ch[p][1] == x else 0
        b = self.ch[x][d ^ 1]
        if not self._is_aux_root(p):
            self.ch[g][1 if self.ch[g][1] == p else 0] = x
        self.fa[x] = g
        self.ch[x][d ^ 1] = p
        self.fa[p] = x
        self.ch[p][d] = b
        if b != -1:
            self.fa[b] = p
        self._pull(p)
        self._pull(x)

    def _splay(self, x):
        stack = [x]
        y = x
        while not self._is_aux_root(y):
            y = self.fa[y]
            stack.append(y)
        for y in reversed(stack):
            self._push(y)
        while not self._is_aux_root(x):
            p = self.fa[x]
            if not self._is_aux_root(p):
                g = self.fa[p]
                zigzig = (self.ch[p][0] == x) == (self.ch[g][0] == p)
                self._rotate(p if zigzig else x)
            self._rotate(x)

    def _access(self, x):
        last = -1
        y = x
        while y != -1:
            self._splay(y)
            self.ch[y][1] = last
            if last != -1:
                self.fa[last] = y
            self._pull(y)
            last = y
            y = self.fa[y]
        self._splay(x)

    def make_root(self, x):
        self._access(x)
        self._apply_rev(x)

    def find_root(self, x):
        self._access(x)
        while True:
            self._push(x)
            left = self.ch[x][0]
            if left == -1:
                break
            x = left
        self._splay(x)
        return x

    def connected(self, u, v):
        if u == v:
            return True
        return self.find_root(u) == self.find_root(v)

    def link(self, u, v):
        self.make_root(u)
        if self.find_root(v) == u:
            return False
        self.fa[u] = v
        return True

    def cut(self, u, v):
        self.make_root(u)
        self._access(v)
        if self.ch[v][0] != u or self.ch[u][1] != -1:
            return False
        self.ch[v][0] = -1
        self.fa[u] = -1
        self._pull(v)
        return True

    def set_value(self, x, value):
        self._access(x)
        self.val[x] = value
        self._pull(x)

    def path_sum(self, u, v):
        if not self.connected(u, v):
            raise ValueError("u and v are disconnected")
        self.make_root(u)
        self._access(v)
        return self.sm[v]

    def path_max(self, u, v):
        if not self.connected(u, v):
            raise ValueError("u and v are disconnected")
        self.make_root(u)
        self._access(v)
        return self.mx[v]

    def path_min(self, u, v):
        if not self.connected(u, v):
            raise ValueError("u and v are disconnected")
        self.make_root(u)
        self._access(v)
        return self.mn[v]

"""Discrete Li Chao tree for minimum of lines y = m*x + b.

Pass all possible query x-coordinates at construction time. Query coordinates
must be present in xs. For maximum queries, negate m, b, and the returned value.
"""
from bisect import bisect_left

class LiChaoTree:
    def __init__(self, xs, inf=10**100):
        self.xs = sorted(set(xs))
        if not self.xs:
            raise ValueError("xs must not be empty")
        self.n = len(self.xs)
        self.inf = inf
        self.tree = [None] * (4 * self.n)

    @staticmethod
    def _eval(line, x):
        return line[0] * x + line[1]

    def add_line(self, m, b):
        self._add(1, 0, self.n, (m, b))

    def _add(self, node, lo, hi, line):
        cur = self.tree[node]
        if cur is None:
            self.tree[node] = line
            return
        mid = (lo + hi) >> 1
        x_l, x_m = self.xs[lo], self.xs[mid]
        if self._eval(line, x_m) < self._eval(cur, x_m):
            self.tree[node], line = line, cur
            cur = self.tree[node]
        if hi - lo == 1:
            return
        # After the midpoint winner is fixed in the node, the other line can
        # beat it on at most one side because both functions are linear.
        if self._eval(line, x_l) < self._eval(cur, x_l):
            self._add(node * 2, lo, mid, line)
        elif self._eval(line, self.xs[hi - 1]) < self._eval(cur, self.xs[hi - 1]):
            self._add(node * 2 + 1, mid, hi, line)

    def query(self, x):
        idx = bisect_left(self.xs, x)
        if idx == self.n or self.xs[idx] != x:
            raise ValueError("query x must be included in xs")
        node, lo, hi = 1, 0, self.n
        ans = self.inf
        while True:
            line = self.tree[node]
            if line is not None:
                ans = min(ans, self._eval(line, x))
            if hi - lo == 1:
                break
            mid = (lo + hi) >> 1
            if idx < mid:
                node, hi = node * 2, mid
            else:
                node, lo = node * 2 + 1, mid
        return ans


class MonotoneCHT:
    """Minimum CHT with strictly increasing slopes and arbitrary x queries.

    Lines are inserted in nondecreasing slope order. Duplicate slopes are
    handled by keeping only the smaller intercept. Queries may be in any order;
    binary search makes each query O(log number_of_lines).
    """
    def __init__(self):
        self.lines = []

    @staticmethod
    def _value(line, x):
        return line[0] * x + line[1]

    @staticmethod
    def _redundant(a, b, c):
        # slopes a.m < b.m < c.m; b is redundant if x_ab <= x_bc
        return (a[1] - b[1]) * (c[0] - b[0]) <= (b[1] - c[1]) * (b[0] - a[0])

    def add_line(self, m, b):
        if self.lines and m < self.lines[-1][0]:
            raise ValueError("slopes must be added in nondecreasing order")
        if self.lines and m == self.lines[-1][0]:
            if b >= self.lines[-1][1]:
                return
            self.lines.pop()
        new = (m, b)
        while len(self.lines) >= 2 and self._redundant(self.lines[-2], self.lines[-1], new):
            self.lines.pop()
        self.lines.append(new)

    def query(self, x):
        if not self.lines:
            raise ValueError("no lines have been inserted")
        lo, hi = 0, len(self.lines) - 1
        while lo < hi:
            mid = (lo + hi) >> 1
            if self._value(self.lines[mid], x) <= self._value(self.lines[mid + 1], x):
                hi = mid
            else:
                lo = mid + 1
        return self._value(self.lines[lo], x)
"""Heavy-Light Decomposition for a tree.

Vertices are 0-indexed. All segment intervals are half-open [l, r).
`path_segments(u, v)` returns (l, r, reversed) pieces in the order from u to v.
For commutative queries, the `reversed` flag can be ignored.
"""

class HLD:
    def __init__(self, n, graph, root=0):
        self.n = n
        self.g = graph
        self.root = root
        self.parent = [-1] * n
        self.depth = [0] * n
        self.size = [1] * n
        self.heavy = [-1] * n

        order = [root]
        self.parent[root] = root
        for v in order:
            for to in graph[v]:
                if to == self.parent[v]:
                    continue
                self.parent[to] = v
                self.depth[to] = self.depth[v] + 1
                order.append(to)
        if len(order) != n:
            raise ValueError("graph must be a connected tree")
        self.parent[root] = -1

        for v in reversed(order):
            best_size = 0
            for to in graph[v]:
                if self.parent[to] == v:
                    self.size[v] += self.size[to]
                    if self.size[to] > best_size:
                        best_size = self.size[to]
                        self.heavy[v] = to

        self.head = [-1] * n
        self.pos = [-1] * n
        self.inv = [-1] * n
        cur = 0
        stack = [(root, root)]
        while stack:
            v, h = stack.pop()
            while v != -1:
                self.head[v] = h
                self.pos[v] = cur
                self.inv[cur] = v
                cur += 1
                # Light subtrees are postponed; heavy child is walked immediately.
                for to in graph[v]:
                    if self.parent[to] == v and to != self.heavy[v]:
                        stack.append((to, to))
                v = self.heavy[v]

        self.out = [self.pos[v] + self.size[v] for v in range(n)]

    def is_ancestor(self, u, v):
        return self.pos[u] <= self.pos[v] < self.out[u]

    def subtree_interval(self, v):
        """Return the Euler-order interval of v's subtree: [pos[v], out[v])."""
        return self.pos[v], self.out[v]

    def lca(self, u, v):
        while self.head[u] != self.head[v]:
            if self.depth[self.head[u]] > self.depth[self.head[v]]:
                u = self.parent[self.head[u]]
            else:
                v = self.parent[self.head[v]]
        return u if self.depth[u] < self.depth[v] else v

    def distance(self, u, v):
        w = self.lca(u, v)
        return self.depth[u] + self.depth[v] - 2 * self.depth[w]

    def path_segments(self, u, v, edge=False):
        """Return ordered path pieces (l, r, reversed) from u to v.

        A piece covers base-array positions [l, r). If `reversed` is True,
        traverse the piece from r-1 down to l. With edge=True, each edge is
        represented by its deeper endpoint, so the LCA position is excluded.
        """
        left, right = [], []
        while self.head[u] != self.head[v]:
            if self.depth[self.head[u]] >= self.depth[self.head[v]]:
                left.append((self.pos[self.head[u]], self.pos[u] + 1, True))
                u = self.parent[self.head[u]]
            else:
                right.append((self.pos[self.head[v]], self.pos[v] + 1, False))
                v = self.parent[self.head[v]]
        if self.depth[u] >= self.depth[v]:
            l = self.pos[v] + (1 if edge else 0)
            r = self.pos[u] + 1
            if l < r:
                left.append((l, r, True))
        else:
            l = self.pos[u] + (1 if edge else 0)
            r = self.pos[v] + 1
            if l < r:
                right.append((l, r, False))
        return left + right[::-1]

    def path_intervals(self, u, v, edge=False):
        """Return unordered path intervals; intended for commutative operations."""
        return [(l, r) for l, r, _ in self.path_segments(u, v, edge)]