結果

問題 No.1720 Division Permutation
ユーザー zkouzkou
提出日時 2021-10-17 15:17:13
言語 PyPy3
(7.3.15)
結果
AC  
実行時間 1,505 ms / 4,000 ms
コード長 15,874 bytes
コンパイル時間 151 ms
コンパイル使用メモリ 82,260 KB
実行使用メモリ 238,088 KB
最終ジャッジ日時 2024-09-23 03:56:00
合計ジャッジ時間 52,449 ms
ジャッジサーバーID
(参考情報)
judge1 / judge5
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 64 ms
69,680 KB
testcase_01 AC 65 ms
71,288 KB
testcase_02 AC 66 ms
70,964 KB
testcase_03 AC 64 ms
70,900 KB
testcase_04 AC 65 ms
70,544 KB
testcase_05 AC 65 ms
70,448 KB
testcase_06 AC 66 ms
70,668 KB
testcase_07 AC 64 ms
70,308 KB
testcase_08 AC 66 ms
71,136 KB
testcase_09 AC 65 ms
70,228 KB
testcase_10 AC 66 ms
69,976 KB
testcase_11 AC 65 ms
69,600 KB
testcase_12 AC 65 ms
70,484 KB
testcase_13 AC 1,466 ms
226,444 KB
testcase_14 AC 1,212 ms
215,456 KB
testcase_15 AC 966 ms
165,724 KB
testcase_16 AC 1,205 ms
214,448 KB
testcase_17 AC 882 ms
172,124 KB
testcase_18 AC 974 ms
238,088 KB
testcase_19 AC 1,503 ms
210,484 KB
testcase_20 AC 1,445 ms
198,456 KB
testcase_21 AC 1,443 ms
201,396 KB
testcase_22 AC 1,472 ms
197,760 KB
testcase_23 AC 1,441 ms
215,084 KB
testcase_24 AC 1,488 ms
203,324 KB
testcase_25 AC 1,463 ms
199,472 KB
testcase_26 AC 1,459 ms
198,200 KB
testcase_27 AC 1,439 ms
193,324 KB
testcase_28 AC 1,427 ms
201,016 KB
testcase_29 AC 1,505 ms
226,488 KB
testcase_30 AC 1,453 ms
207,032 KB
testcase_31 AC 1,476 ms
196,916 KB
testcase_32 AC 66 ms
69,584 KB
testcase_33 AC 92 ms
78,136 KB
testcase_34 AC 67 ms
71,116 KB
testcase_35 AC 91 ms
78,288 KB
testcase_36 AC 68 ms
72,520 KB
testcase_37 AC 67 ms
71,160 KB
testcase_38 AC 100 ms
78,192 KB
testcase_39 AC 73 ms
73,568 KB
testcase_40 AC 65 ms
70,268 KB
testcase_41 AC 92 ms
78,412 KB
testcase_42 AC 76 ms
74,304 KB
testcase_43 AC 1,266 ms
177,936 KB
testcase_44 AC 1,021 ms
165,648 KB
testcase_45 AC 975 ms
164,764 KB
testcase_46 AC 818 ms
139,964 KB
testcase_47 AC 998 ms
162,532 KB
testcase_48 AC 818 ms
139,816 KB
testcase_49 AC 1,204 ms
175,320 KB
testcase_50 AC 1,006 ms
158,980 KB
testcase_51 AC 846 ms
141,604 KB
testcase_52 AC 1,077 ms
166,032 KB
testcase_53 AC 1,247 ms
188,604 KB
testcase_54 AC 1,253 ms
221,496 KB
testcase_55 AC 1,208 ms
183,604 KB
testcase_56 AC 1,207 ms
182,452 KB
testcase_57 AC 1,270 ms
219,900 KB
testcase_58 AC 1,222 ms
191,288 KB
testcase_59 AC 1,265 ms
187,184 KB
testcase_60 AC 1,206 ms
217,060 KB
testcase_61 AC 1,250 ms
187,872 KB
testcase_62 AC 1,240 ms
182,488 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

from typing import List, Tuple


class lazy_segtree:
    """It is the data structure for the pair of a monoid (S, op) and a set F of S to S mappings that satisfies the following properties.

    >   F contains the identity map id, where the identity map is the map that satisfies id(x) = x for all x in S.

    >   F is closed under composition, i.e., `composition(f, g)` is in F for all `f`, `g` in F.

    >   `f(op(x, y)) = op(f(x), f(y))` holds for all `f` in F and `x`, `y` in S.

    Given an array of length n, it processes the following queries in O(log n) time
    (see Appendix in the document of AC Library for further details).

    >   Acting the map f in F (cf. x = f(x)) on all the elements of an interval

    >   Calculating the product of the elements of an interval

    For simplicity, in this document, we assume that the oracles `op`, `e`, `mapping`, `composition`, and `id_` work in constant time. 
    If these oracles work in O(T) time, each time complexity appear in this document is multipled by O(T).
    """

    __slots__ = ["op", "e", "mapping", "composition",
                 "id_", "n", "log", "size", "d", "lz"]

    def __init__(self, n_or_list, op, e, mapping, composition, id_):
        """The following should be defined.

        >   The binary operation `op(a, b)`

        >   The function `e()` that returns the identity element

        >   The function `mapping(f, x)` that returns f(x)

        >   The function `composition(f, g)` that returns f composed with g

        >   The function `id_()` that returns the identity map

        For example, for Range Multiplication Range Sum Query, the definitions are as follows.

        ```python
        seg = lazy_segtree(10, lambda a, b: a+b, lambda: 0, lambda f, x: f*x, lambda f, g: f*g, lambda: 1)
        ```

        >   If `n_or_list` is an integer, it creates an array `a` of length `n_or_list`. All the elements are initialized to `e()`.

        >   If `n_or_list` is a list, it creates an array `a` of length `len(n_or_list)`, initialized to `n_or_list`.

        >   Otherwise, it raises `TypeError`.

        Constraints
        -----------

        >   0 <= n <= 10 ** 8

        Complexity
        ----------

        >   O(n)
        """
        self.op = op
        self.e = e
        self.mapping = mapping
        self.composition = composition
        self.id_ = id_
        if isinstance(n_or_list, int):
            self.n = n_or_list
            self.log = (self.n - 1).bit_length()
            self.size = 1 << self.log
            self.d = [self.e() for _ in range(self.size * 2)]
            self.lz = [self.id_() for _ in range(self.size)]
        elif isinstance(n_or_list, list):
            self.n = len(n_or_list)
            self.log = (self.n - 1).bit_length()
            self.size = 1 << self.log
            self.d = [self.e() for _ in range(self.size)] + n_or_list + \
                [self.e() for _ in range(self.size - self.n)]
            for i in range(self.size - 1, 0, -1):
                self.d[i] = self.op(self.d[2*i], self.d[2*i+1])
            self.lz = [self.id_() for _ in range(self.size)]
        else:
            raise TypeError(
                f"The argument 'n_or_list' must be an integer or a list, not {type(n_or_list).__name__}")

    def set(self, p, x):
        """It assigns x to `a[p]`.

        Constraints
        -----------

        >   0 <= p < n

        Complexity
        ----------

        >   O(log n)
        """
        # assert 0 <= p < self.n
        p += self.size
        for i in range(self.log, 0, -1):
            self._push(p >> i)
        self.d[p] = x
        for i in range(1, self.log + 1):
            self._update(p >> i)

    def get(self, p):
        """It returns `a[p]`.

        Constraints
        -----------

        >   0 <= p < n

        Complexity
        ----------

        >   O(log n)
        """
        # assert 0 <= p < self.n
        p += self.size
        for i in range(self.log, 0, -1):
            self._push(p >> i)
        return self.d[p]

    def prod(self, l, r):
        """It returns `op(a[l], ..., a[r - 1])`, assuming the properties of the monoid. 
        It returns `e()` if l = r.

        Constraints
        -----------

        >   0 <= l <= r <= n

        Complexity
        ----------

        >   O(log n)
        """
        # assert 0 <= l <= r <= self.n
        if l == r:
            return self.e()
        l += self.size
        r += self.size

        indices = []
        l0 = (l // (l & -l)) // 2
        r0 = (r // (r & -r) - 1) // 2
        while l0 != r0:
            if l0 < r0:
                indices.append(r0)
                r0 //= 2
            else:
                indices.append(l0)
                l0 //= 2
        while r0:
            indices.append(r0)
            r0 //= 2

        for index in reversed(indices):
            self._push(index)

        sml = self.e()
        smr = self.e()
        while l < r:
            if l % 2:
                sml = self.op(sml, self.d[l])
                l += 1
            if r % 2:
                r -= 1
                smr = self.op(self.d[r], smr)
            l //= 2
            r //= 2

        return self.op(sml, smr)

    def all_prod(self):
        """It returns `op(a[0], ..., a[n - 1])`, assuming the properties of the monoid.
        It returns `e()` if n = 0.

        Complexity
        ----------

        >   O(1)
        """
        return self.d[1]

    def point_apply(self, p, f):
        """It applies `a[p] = f(a[p])`.

        Constraints
        -----------

        >   0 <= p < n

        Complexity
        ----------

        >   O(log n)
        """
        # assert 0 <= p <= self.n
        p += self.size
        for i in range(self.log, 0, -1):
            self._push(p >> i)
        self.d[p] = self.mapping(f, self.d[p])
        for i in range(1, self.log + 1):
            self._update(p >> i)

    def apply(self, l, r, f):
        """It applies `a[i] = f(a[i])` for all `i` with `l <= i < r`.

        Constraints
        -----------

        >   0 <= l <= r <= n

        Complexity
        ----------

        >   O(log n)
        """
        # assert 0 <= l <= r <= self.n
        if l == r:
            return
        l += self.size
        r += self.size

        indices = []
        l0 = (l // (l & -l)) // 2
        r0 = (r // (r & -r) - 1) // 2
        while l0 != r0:
            if l0 < r0:
                indices.append(r0)
                r0 //= 2
            else:
                indices.append(l0)
                l0 //= 2
        while r0:
            indices.append(r0)
            r0 //= 2

        for index in reversed(indices):
            self._push(index)

        while l < r:
            if l % 2:
                self._all_apply(l, f)
                l += 1
            if r % 2:
                r -= 1
                self._all_apply(r, f)
            l //= 2
            r //= 2

        for index in indices:
            self._update(index)

    def max_right(self, l, g):
        """It returns an index `r` that satisfies both of the following.

        >   `r == l` or `g(op(a[l], a[l + 1], ..., a[r - 1])) == True`

        >   `r == n` or `g(op(a[l], a[l + 1], ..., a[r])) == False`

        If `g` is monotone, this is the maximum `r` that satisfies `g(op(a[l], a[l + 1], ..., a[r - 1])) == True`.

        Constraints
        -----------

        >   if `g` is called with the same argument, it returns the same value, i.e., `g` has no side effect.

        >   `g(e()) == True`

        >   0 <= l <= n

        Complexity
        ----------

        >   O(log n)
        """
        # assert 0 <= l <= self.n
        # assert g(self.e())
        if l == self.n:
            return self.n
        l += self.size
        for i in range(self.log, 0, -1):
            self._push(l >> i)
        sm = self.e()
        while True:
            while l % 2 == 0:
                l //= 2
            if not g(self.op(sm, self.d[l])):
                while l < self.size:
                    self._push(l)
                    l *= 2
                    if g(self.op(sm, self.d[l])):
                        sm = self.op(sm, self.d[l])
                        l += 1
                return l - self.size
            sm = self.op(sm, self.d[l])
            l += 1
            if l == l & -l:
                break
        return self.n

    def min_left(self, r, g):
        """It returns an index `l` that satisfies both of the following.

        >   `l == r` or `g(op(a[l], a[l + 1], ..., a[r - 1])) == True`

        >   `l == 0` or `g(op(a[l - 1], a[l], ..., a[r - 1])) == False`

        If `g` is monotone, this is the minimum `l` that satisfies `g(op(a[l], a[l + 1], ..., a[r - 1])) == True`.

        Constraints
        -----------

        >   if `g` is called with the same argument, it returns the same value, i.e., `g` has no side effect.

        >   `g(e()) == True`

        >   0 <= r <= n

        Complexity
        ----------

        >   O(log n)
        """
        # assert 0 <= r <= self.n
        # assert g(self.e())
        if r == 0:
            return 0
        r += self.size
        for i in range(self.log, 0, -1):
            self._push((r - 1) >> i)
        sm = self.e()
        while True:
            r -= 1
            while (r > 1 and r % 2):
                r //= 2
            if not g(self.op(self.d[r], sm)):
                while r < self.size:
                    self._push(r)
                    r = 2 * r + 1
                    if g(self.op(self.d[r], sm)):
                        sm = self.op(self.d[r], sm)
                        r -= 1
                return r + 1 - self.size
            sm = self.op(self.d[r], sm)
            if r == r & -r:
                break
        return 0

    def _update(self, k):
        self.d[k] = self.op(self.d[2 * k], self.d[2 * k + 1])

    def _all_apply(self, k, f):
        self.d[k] = self.mapping(f, self.d[k])
        if k < self.size:
            self.lz[k] = self.composition(f, self.lz[k])

    def _push(self, k):
        self._all_apply(2 * k, self.lz[k])
        self._all_apply(2 * k + 1, self.lz[k])
        self.lz[k] = self.id_()


class permutation_tree:

    def __init__(self, vec: List[int]) -> None:
        self.vec = vec
        self.build()

    def get_range(self, x: Tuple[int, int], y: Tuple[int, int]) -> Tuple[int, int]:
        return (min(x[0], y[0]), max(x[1], y[1]))

    def adj(self, i: int, j: int) -> bool:
        return self.range_[i][1] == self.range_[j][0]

    def length(self, i: int) -> int:
        return self.range_[i][1] - self.range_[i][0]

    def add_edge(self, p: int, c: int) -> None:
        self.parent[c] = p
        self.children[p].append(c)

    def build(self) -> None:
        max_stack = [-1]
        min_stack = [-1]
        nodes = []
        self.sz = n = len(self.vec)
        self.parent = [-1] * (n * 2)
        self.children = [[] for _ in range(n * 2)]
        self.range_ = [(0, 0)] * (n * 2)
        self.span = [(0, 0)] * (n * 2)
        self.typ = [0] * (n * 2)
        seg = lazy_segtree([0] * (n * 2), min, lambda: 1 << 30,
                           lambda f, x: f + x, lambda f, g: f + g, lambda: 0)

        for i in range(n):
            while max_stack[-1] != -1 and self.vec[max_stack[-1]] < self.vec[i]:
                tmp = max_stack.pop()
                seg.apply(max_stack[-1] + 1, tmp + 1,
                          self.vec[i] - self.vec[tmp])
            max_stack.append(i)

            while min_stack[-1] != -1 and self.vec[min_stack[-1]] > self.vec[i]:
                tmp = min_stack.pop()
                seg.apply(min_stack[-1] + 1, tmp + 1,
                          self.vec[tmp] - self.vec[i])
            min_stack.append(i)

            self.range_[i] = (self.vec[i], self.vec[i] + 1)
            self.span[i] = (i, i + 1)

            cur = i

            while True:
                if nodes and (self.adj(nodes[-1], cur) or self.adj(cur, nodes[-1])):
                    if (self.adj(nodes[-1], cur) and self.typ[nodes[-1]] == 2) or (self.adj(cur, nodes[-1]) and self.typ[nodes[-1]] == 1):
                        self.add_edge(nodes[-1], cur)
                        self.range_[
                            nodes[-1]] = self.get_range(self.range_[nodes[-1]], self.range_[cur])
                        self.span[nodes[-1]] = self.get_range(
                            self.span[nodes[-1]], self.span[cur])
                        cur = nodes.pop()
                    else:
                        self.add_edge(self.sz, nodes[-1])
                        self.add_edge(self.sz, cur)
                        self.typ[self.sz] = 2 if self.adj(
                            nodes[-1], cur) else 1
                        self.range_[self.sz] = self.get_range(
                            self.range_[nodes[-1]], self.range_[cur])
                        self.span[self.sz] = self.get_range(
                            self.span[nodes[-1]], self.span[cur])
                        cur = self.sz
                        self.sz += 1
                        nodes.pop()
                elif i + 1 - self.length(cur) and seg.prod(0, i + 1 - self.length(cur)) == 0:
                    len_ = self.length(cur)
                    rng = self.range_[cur]
                    sp = self.span[cur]
                    self.add_edge(self.sz, cur)
                    while True:
                        len_ += self.length(nodes[-1])
                        rng = self.get_range(rng, self.range_[nodes[-1]])
                        sp = self.get_range(sp, self.span[nodes[-1]])
                        self.add_edge(self.sz, nodes[-1])
                        nodes.pop()
                        if rng[1] - rng[0] == len_:
                            break
                    self.children[self.sz].reverse()
                    self.range_[self.sz] = rng
                    self.span[self.sz] = sp
                    cur = self.sz
                    self.sz += 1
                else:
                    break
            nodes.append(cur)
            seg.apply(0, i + 1, -1)
        self.shrink()

    def shrink(self) -> None:
        self.parent = self.parent[:self.sz]
        self.children = self.children[:self.sz]
        self.range_ = self.range_[:self.sz]
        self.span = self.span[:self.sz]
        self.typ = self.typ[:self.sz]


MOD = 998244353

N, K = map(int, input().split())
Ps = list(map(int, input().split()))


def add(f, g):
    if len(f) < len(g):
        f, g = g, f
    ans = f.copy()
    for i, gi in enumerate(g):
        ans[i] += gi
        ans[i] %= MOD
    return ans[:K+1]


def multiply(f, g):
    ans = [0] * (len(f) + len(g) - 1)
    for i, fi in enumerate(f):
        for j, gj in enumerate(g):
            ans[i + j] += fi * gj
            ans[i + j] %= MOD
    return ans[:K+1]


perm_tree = permutation_tree(Ps)

dp = [[] for _ in range(perm_tree.sz)]
root = perm_tree.parent.index(-1)
# print(root)
stack = [~root, root]
while stack:
    i = stack.pop()
    if i >= 0:
        for ni in perm_tree.children[i]:
            stack.append(~ni)
            stack.append(ni)
    else:
        i = ~i
        if 0 <= i < N:
            dp[i] = [0, 1]
            continue
        t = perm_tree.typ[i]
        cs = perm_tree.children[i]
        if t:   
            csum = [[1]]
            ans = [1]
            for j, c in enumerate(cs):
                ans = multiply(ans, dp[c])
                if j >= 1:
                    ans = add(ans, [0] + csum[j-1]) 
                csum.append(add(csum[-1], ans))
            dp[i] = ans     
        else:
            t = [1]
            for c in cs:
                t = multiply(t, dp[c])
            t = add(t, [0, 1])
            dp[i] = t
        
ans = dp[root]
while len(ans) < K + 1:
    ans.append(0)

print(*ans[1:], sep='\n')
0