結果

問題 No.1720 Division Permutation
ユーザー hitonanodehitonanode
提出日時 2021-10-24 13:42:12
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 175 ms / 4,000 ms
コード長 9,112 bytes
コンパイル時間 1,311 ms
コンパイル使用メモリ 105,204 KB
実行使用メモリ 25,440 KB
最終ジャッジ日時 2024-10-02 01:23:33
合計ジャッジ時間 9,582 ms
ジャッジサーバーID
(参考情報)
judge1 / judge2
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2 ms
5,248 KB
testcase_01 AC 1 ms
5,248 KB
testcase_02 AC 1 ms
5,248 KB
testcase_03 AC 1 ms
5,248 KB
testcase_04 AC 2 ms
5,248 KB
testcase_05 AC 1 ms
5,248 KB
testcase_06 AC 2 ms
5,248 KB
testcase_07 AC 2 ms
5,248 KB
testcase_08 AC 1 ms
5,248 KB
testcase_09 AC 2 ms
5,248 KB
testcase_10 AC 2 ms
5,248 KB
testcase_11 AC 1 ms
5,248 KB
testcase_12 AC 2 ms
5,248 KB
testcase_13 AC 160 ms
24,644 KB
testcase_14 AC 159 ms
24,648 KB
testcase_15 AC 93 ms
16,508 KB
testcase_16 AC 153 ms
23,820 KB
testcase_17 AC 109 ms
21,636 KB
testcase_18 AC 102 ms
24,348 KB
testcase_19 AC 159 ms
25,000 KB
testcase_20 AC 157 ms
24,876 KB
testcase_21 AC 156 ms
24,744 KB
testcase_22 AC 155 ms
25,000 KB
testcase_23 AC 153 ms
25,260 KB
testcase_24 AC 159 ms
25,008 KB
testcase_25 AC 156 ms
24,748 KB
testcase_26 AC 160 ms
24,748 KB
testcase_27 AC 161 ms
24,876 KB
testcase_28 AC 157 ms
24,872 KB
testcase_29 AC 158 ms
25,160 KB
testcase_30 AC 156 ms
24,744 KB
testcase_31 AC 157 ms
24,752 KB
testcase_32 AC 2 ms
5,248 KB
testcase_33 AC 1 ms
5,248 KB
testcase_34 AC 1 ms
5,248 KB
testcase_35 AC 2 ms
5,248 KB
testcase_36 AC 2 ms
5,248 KB
testcase_37 AC 2 ms
5,248 KB
testcase_38 AC 1 ms
5,248 KB
testcase_39 AC 2 ms
5,248 KB
testcase_40 AC 2 ms
5,248 KB
testcase_41 AC 1 ms
5,248 KB
testcase_42 AC 1 ms
5,248 KB
testcase_43 AC 140 ms
22,236 KB
testcase_44 AC 129 ms
21,832 KB
testcase_45 AC 118 ms
21,968 KB
testcase_46 AC 85 ms
15,540 KB
testcase_47 AC 123 ms
21,892 KB
testcase_48 AC 89 ms
16,340 KB
testcase_49 AC 139 ms
22,672 KB
testcase_50 AC 112 ms
21,732 KB
testcase_51 AC 89 ms
16,120 KB
testcase_52 AC 132 ms
21,728 KB
testcase_53 AC 156 ms
25,284 KB
testcase_54 AC 159 ms
24,640 KB
testcase_55 AC 161 ms
25,136 KB
testcase_56 AC 162 ms
25,304 KB
testcase_57 AC 163 ms
24,768 KB
testcase_58 AC 165 ms
25,440 KB
testcase_59 AC 175 ms
25,220 KB
testcase_60 AC 165 ms
24,532 KB
testcase_61 AC 165 ms
25,412 KB
testcase_62 AC 162 ms
25,132 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#line 1 "other_algorithms/test/permutation_tree.yuki1720.test.cpp"
#define PROBLEM "https://yukicoder.me/problems/no/1720"
#line 2 "segmenttree/range-add-range-min.hpp"
#include <algorithm>
#include <limits>
#include <vector>

// CUT begin
// StarrySkyTree: segment tree for Range Minimum Query & Range Add Query
// Complexity: $O(N)$ (construction), $O(\log N)$ (add / get / prod)
// - RangeAddRangeMin(std::vector<Tp> data_init) : Initialize array x by data_init.
// - add(int begin, int end, Tp vadd) : Update x[i] <- x[i] + vadd for all begin <= i < end.
// - get(int pos) : Get x[pos].
// - prod(int begin, int end) : Get min(x[begin], ..., x[end - 1]).
template <typename Tp, Tp defaultT = std::numeric_limits<Tp>::max() / 2> struct RangeAddRangeMin {
    int N, head;
    std::vector<Tp> range_min, range_add;
    static inline Tp f(Tp x, Tp y) noexcept { return std::min(x, y); }

    inline void _merge(int pos) {
        range_min[pos] = f(range_min[pos * 2] + range_add[pos * 2],
                           range_min[pos * 2 + 1] + range_add[pos * 2 + 1]);
    }
    void initialize(const std::vector<Tp> &data_init) {
        N = data_init.size(), head = 1;
        while (head < N) head <<= 1;
        range_min.assign(head * 2, defaultT);
        range_add.assign(head * 2, 0);
        std::copy(data_init.begin(), data_init.end(), range_min.begin() + head);
        for (int pos = head; --pos;) _merge(pos);
    }
    RangeAddRangeMin() = default;
    RangeAddRangeMin(const std::vector<Tp> &data_init) { initialize(data_init); }
    void _add(int begin, int end, int pos, int l, int r, Tp vadd) noexcept {
        if (r <= begin or end <= l) return;
        if (begin <= l and r <= end) {
            range_add[pos] += vadd;
            return;
        }
        _add(begin, end, pos * 2, l, (l + r) / 2, vadd);
        _add(begin, end, pos * 2 + 1, (l + r) / 2, r, vadd);
        _merge(pos);
    }
    // Add `vadd` to (x_begin, ..., x_{end - 1})
    void add(int begin, int end, Tp vadd) noexcept { return _add(begin, end, 1, 0, head, vadd); }
    Tp _get(int begin, int end, int pos, int l, int r) const noexcept {
        if (r <= begin or end <= l) return defaultT;
        if (begin <= l and r <= end) return range_min[pos] + range_add[pos];
        return f(_get(begin, end, pos * 2, l, (l + r) / 2),
                 _get(begin, end, pos * 2 + 1, (l + r) / 2, r)) +
               range_add[pos];
    }
    // Return f(x_begin, ..., x_{end - 1})
    Tp get(int pos) const noexcept { return prod(pos, pos + 1); }
    Tp prod(int begin, int end) const noexcept { return _get(begin, end, 1, 0, head); }
};
#line 4 "other_algorithms/permutation_tree.hpp"
#include <cassert>
#include <fstream>
#include <string>
#line 8 "other_algorithms/permutation_tree.hpp"

// Permutation tree
// Complexity: O(N log N)
// https://codeforces.com/blog/entry/78898 https://yukicoder.me/problems/no/1720
struct permutation_tree {
    enum NodeType {
        JoinAsc,
        JoinDesc,
        Cut,
        Leaf,
        None,
    };
    struct node {
        NodeType tp;
        int L, R;       // i in [L, R)
        int mini, maxi; // A[i] in [mini, maxi]
        std::vector<int> child;
        int sz() const { return R - L; }
        template <class OStream> friend OStream &operator<<(OStream &os, const node &n) {
            os << "[[" << n.L << ',' << n.R << ")(ch:";
            for (auto i : n.child) os << i << ',';
            return os << ")(tp=" << n.tp << ")]";
        }
    };

    int root;
    std::vector<int> A;
    std::vector<node> nodes;

    void _add_child(int parid, int chid) {
        nodes[parid].child.push_back(chid);
        nodes[parid].L = std::min(nodes[parid].L, nodes[chid].L);
        nodes[parid].R = std::max(nodes[parid].R, nodes[chid].R);
        nodes[parid].mini = std::min(nodes[parid].mini, nodes[chid].mini);
        nodes[parid].maxi = std::max(nodes[parid].maxi, nodes[chid].maxi);
    }

    permutation_tree() : root(-1) {}
    permutation_tree(const std::vector<int> &A_) : root(-1), A(A_) { // A: nonempty perm., 0-origin
        assert(!A.empty());
        RangeAddRangeMin<int> seg((std::vector<int>(A.size())));

        std::vector<int> hi{-1}, lo{-1};
        std::vector<int> st;
        for (int i = 0; i < int(A.size()); ++i) {
            while (hi.back() >= 0 and A[i] > A[hi.back()]) {
                seg.add(hi[hi.size() - 2] + 1, hi.back() + 1, A[i] - A[hi.back()]);
                hi.pop_back();
            }
            hi.push_back(i);
            while (lo.back() >= 0 and A[i] < A[lo.back()]) {
                seg.add(lo[lo.size() - 2] + 1, lo.back() + 1, A[lo.back()] - A[i]);
                lo.pop_back();
            }
            lo.push_back(i);

            int h = nodes.size();
            nodes.push_back({NodeType::Leaf, i, i + 1, A[i], A[i], std::vector<int>{}});

            while (true) {
                NodeType join_tp = NodeType::None;
                if (!st.empty() and nodes[st.back()].maxi + 1 == nodes[h].mini) join_tp = JoinAsc;
                if (!st.empty() and nodes[h].maxi + 1 == nodes[st.back()].mini) join_tp = JoinDesc;

                if (!st.empty() and join_tp != NodeType::None) {
                    const node &vtp = nodes[st.back()];
                    // Insert v as the child of the top node in the stack
                    if (join_tp == vtp.tp) {
                        // Append child to existing Join node
                        _add_child(st.back(), h);
                        h = st.back();
                        st.pop_back();
                    } else {
                        // Make new join node (with exactly two children)
                        int j = st.back();
                        nodes.push_back(
                            {join_tp, nodes[j].L, nodes[j].R, nodes[j].mini, nodes[j].maxi, {j}});
                        st.pop_back();
                        _add_child(nodes.size() - 1, h);
                        h = nodes.size() - 1;
                    }
                } else if (seg.prod(0, i + 1 - nodes[h].sz()) == 0) {
                    // Make Cut node
                    int L = nodes[h].L, R = nodes[h].R, maxi = nodes[h].maxi, mini = nodes[h].mini;
                    nodes.push_back({NodeType::Cut, L, R, mini, maxi, {h}});
                    h = nodes.size() - 1;
                    do {
                        _add_child(h, st.back());
                        st.pop_back();
                    } while (nodes[h].maxi - nodes[h].mini + 1 != nodes[h].sz());
                    std::reverse(nodes[h].child.begin(), nodes[h].child.end());
                } else {
                    break;
                }
            }
            st.push_back(h);
            seg.add(0, i + 1, -1);
        }
        assert(st.size() == 1);
        root = st[0];
    }

    void to_DOT(std::string filename = "") const {
        if (filename.empty()) filename = "permutation_tree_" + std::to_string(A.size()) + ".DOT";

        std::ofstream ss(filename);
        ss << "digraph{\n";
        int nleaf = 0;
        for (int i = 0; i < int(nodes.size()); i++) {
            ss << i << "[\n";
            std::string lbl;
            if (nodes[i].tp == NodeType::Leaf) {
                lbl = "A[" + std::to_string(nleaf) + "] = " + std::to_string(A[nleaf]), nleaf++;
            } else {
                lbl += std::string(nodes[i].tp == NodeType::Cut ? "Cut" : "Join") + "\\n";
                lbl += "[" + std::to_string(nodes[i].L) + ", " + std::to_string(nodes[i].R) + ")";
            }
            ss << "label = \"" << lbl << "\",\n";
            ss << "]\n";
            for (const auto &ch : nodes[i].child) ss << i << " -> " << ch << ";\n";
        }
        ss << "{rank = same;";
        for (int i = 0; i < int(nodes.size()); i++) {
            if (nodes[i].tp == NodeType::Leaf) ss << ' ' << i << ';';
        }
        ss << "}\n";
        ss << "}\n";
        ss.close();
        return;
    }
};

#include <atcoder/modint>

#line 5 "other_algorithms/test/permutation_tree.yuki1720.test.cpp"

#include <iostream>
using namespace std;
using mint = atcoder::modint998244353;

int N, K;
permutation_tree tree;
vector<vector<mint>> dp;

void rec(int now) {
    const auto &v = tree.nodes[now];
    if (v.tp == permutation_tree::Cut or v.tp == permutation_tree::Leaf) {
        for (int k = 0; k < K; ++k) dp[k + 1][v.R] += dp[k][v.L];
    }

    vector<mint> sum(K);
    for (auto ch : v.child) {
        rec(ch);
        if (v.tp == permutation_tree::JoinAsc or v.tp == permutation_tree::JoinDesc) {
            for (int k = 0; k < K; ++k) {
                dp[k + 1][tree.nodes[ch].R] += sum[k];
                sum[k] += dp[k][tree.nodes[ch].L];
            }
        }
    }
};

int main() {
    cin.tie(nullptr), ios::sync_with_stdio(false);

    cin >> N >> K;
    vector<int> P(N);

    for (auto &x : P) cin >> x;
    for (auto &x : P) x--;

    tree = permutation_tree(P);

    dp.assign(K + 1, vector<mint>(N + 1));
    dp[0][0] = 1;

    rec(tree.root);

    for (int i = 1; i <= K; i++) cout << dp[i][N].val() << '\n';
}
0