Submission #72329432


Source Code Expand

#include <bits/stdc++.h>
using namespace std;

#pragma GCC optimize("O3", "unroll-loops")

struct Pos {
    int r;
    int c;
};

inline int dist(const Pos &a, const Pos &b) {
    return abs(a.r - b.r) + abs(a.c - b.c);
}

constexpr long long INF = (1LL << 60);

struct Timer {
    using clk = chrono::steady_clock;
    clk::time_point st;
    Timer() : st(clk::now()) {}
    double sec() const {
        return chrono::duration<double>(clk::now() - st).count();
    }
};

struct XorShift64 {
    uint64_t x = 88172645463393265ull;
    uint64_t next_u64() {
        x ^= x << 7;
        x ^= x >> 9;
        return x;
    }
    int next_int(int lo, int hi) {
        return lo + static_cast<int>(next_u64() % (uint64_t)(hi - lo + 1));
    }
    double next_double() {
        return (next_u64() >> 11) * (1.0 / 9007199254740992.0);
    }
};

struct Tree {
    int M;
    int root;
    vector<int> parent;
    vector<vector<int>> children;
};

enum MoveType {
    MOVE_SWAP = 0,
    MOVE_REPARENT = 1,
    MOVE_LABEL_SWAP = 2,
    MOVE_PARENT_SWAP = 3
};

struct Move {
    int type = -1;
    int p = -1;
    int i = -1;
    int j = -1;
    int v = -1;
    int old_parent = -1;
    int old_index = -1;
    int new_parent = -1;
    int new_index = -1;
};

static vector<Pos> center2;
static Pos root_center2;

struct SegNode {
    Pos entry[2];
    Pos exit[2];
    long long cost[2][2];
};

static void fill_leaf_node(SegNode &node, int child, const vector<array<Pos, 2>> &pos,
                           const vector<array<long long, 2>> &cost) {
    node.entry[0] = pos[child][0];
    node.entry[1] = pos[child][1];
    node.exit[0] = pos[child][1];
    node.exit[1] = pos[child][0];
    node.cost[0][0] = cost[child][0];
    node.cost[1][1] = cost[child][1];
    node.cost[0][1] = INF;
    node.cost[1][0] = INF;
}

static SegNode merge_nodes(const SegNode &a, const SegNode &b) {
    SegNode res;
    res.entry[0] = a.entry[0];
    res.entry[1] = a.entry[1];
    res.exit[0] = b.exit[0];
    res.exit[1] = b.exit[1];
    for (int i = 0; i < 2; ++i) {
        for (int k = 0; k < 2; ++k) {
            long long best = INF;
            for (int j = 0; j < 2; ++j) {
                for (int l = 0; l < 2; ++l) {
                    long long cand = a.cost[i][j] + dist(a.exit[j], b.entry[l]) + b.cost[l][k];
                    if (cand < best) best = cand;
                }
            }
            res.cost[i][k] = best;
        }
    }
    return res;
}

struct RootSegTree {
    int n = 0;
    vector<SegNode> seg;
    vector<int> order;
    vector<int> index_of;

    void build(const vector<int> &children, const vector<array<Pos, 2>> &pos,
               const vector<array<long long, 2>> &cost) {
        order = children;
        n = (int)order.size();
        seg.assign(n ? 4 * n : 1, SegNode());
        index_of.assign((int)cost.size(), -1);
        for (int i = 0; i < n; ++i) index_of[order[i]] = i;
        if (n == 0) return;
        build_rec(1, 0, n - 1, pos, cost);
    }

    void build_rec(int idx, int l, int r, const vector<array<Pos, 2>> &pos,
                   const vector<array<long long, 2>> &cost) {
        if (l == r) {
            fill_leaf_node(seg[idx], order[l], pos, cost);
            return;
        }
        int mid = (l + r) / 2;
        build_rec(idx * 2, l, mid, pos, cost);
        build_rec(idx * 2 + 1, mid + 1, r, pos, cost);
        seg[idx] = merge_nodes(seg[idx * 2], seg[idx * 2 + 1]);
    }

    void update_child(int child, const vector<array<Pos, 2>> &pos,
                      const vector<array<long long, 2>> &cost) {
        if (n == 0) return;
        int idx = (child >= 0 && child < (int)index_of.size()) ? index_of[child] : -1;
        if (idx < 0) return;
        update_rec(1, 0, n - 1, idx, pos, cost);
    }

    void update_rec(int node, int l, int r, int idx, const vector<array<Pos, 2>> &pos,
                    const vector<array<long long, 2>> &cost) {
        if (l == r) {
            fill_leaf_node(seg[node], order[l], pos, cost);
            return;
        }
        int mid = (l + r) / 2;
        if (idx <= mid) update_rec(node * 2, l, mid, idx, pos, cost);
        else update_rec(node * 2 + 1, mid + 1, r, idx, pos, cost);
        seg[node] = merge_nodes(seg[node * 2], seg[node * 2 + 1]);
    }

    void swap_positions(int i, int j, const vector<array<Pos, 2>> &pos,
                        const vector<array<long long, 2>> &cost) {
        if (n == 0) return;
        if (i < 0 || j < 0 || i >= n || j >= n || i == j) return;
        int a = order[i];
        int b = order[j];
        swap(order[i], order[j]);
        if (a >= 0 && a < (int)index_of.size()) index_of[a] = j;
        if (b >= 0 && b < (int)index_of.size()) index_of[b] = i;
        update_rec(1, 0, n - 1, i, pos, cost);
        update_rec(1, 0, n - 1, j, pos, cost);
    }

    long long total_cost() const {
        if (n == 0) return 0;
        const SegNode &root = seg[1];
        const long long INF = (1LL << 60);
        Pos start{0, 0};
        long long best = INF;
        for (int s = 0; s < 2; ++s) {
            for (int t = 0; t < 2; ++t) {
                long long cand = dist(start, root.entry[s]) + root.cost[s][t];
                if (cand < best) best = cand;
            }
        }
        return best;
    }
};

static vector<int> build_greedy_order(int M, const vector<array<Pos, 2>> &pos) {
    vector<int> order;
    order.reserve(M);
    vector<char> used(M, 0);
    Pos cur{0, 0};

    for (int step = 0; step < M; ++step) {
        int best = -1;
        int best_cost = INT_MAX;
        int best_end = 0;
        for (int i = 0; i < M; ++i) {
            if (used[i]) continue;
            Pos a = pos[i][0];
            Pos b = pos[i][1];
            int c0 = dist(cur, a) + dist(a, b); // end at b
            int c1 = dist(cur, b) + dist(b, a); // end at a
            int c = min(c0, c1);
            if (c < best_cost) {
                best_cost = c;
                best = i;
                best_end = (c0 <= c1) ? 1 : 0;
            }
        }
        if (best == -1) break;
        order.push_back(best);
        used[best] = 1;
        cur = (best_end == 1) ? pos[best][1] : pos[best][0];
    }

    for (int i = 0; i < M; ++i) {
        if (!used[i]) order.push_back(i);
    }
    return order;
}

static Tree build_initial_tree(int M, const vector<array<Pos, 2>> &pos) {
    Tree tr;
    tr.M = M;
    tr.root = M;
    tr.parent.assign(M, tr.root);
    tr.children.assign(M + 1, {});

    vector<int> order = build_greedy_order(M, pos);
    tr.children[tr.root] = order;
    return tr;
}

static long long evaluate_tree(const Tree &tr, const vector<array<Pos, 2>> &pos,
                               vector<array<long long, 2>> &cost) {
    int M = tr.M;
    int root = tr.root;
    cost.assign(M, {0LL, 0LL});

    vector<int> post;
    post.reserve(M);
    function<void(int)> dfs = [&](int v) {
        for (int u : tr.children[v]) dfs(u);
        if (v != root) post.push_back(v);
    };
    dfs(root);

    for (int v : post) {
        const auto &ch = tr.children[v];
        for (int s = 0; s < 2; ++s) {
            Pos open = pos[v][s];
            Pos close = pos[v][1 - s];
            if (ch.empty()) {
                cost[v][s] = dist(open, close);
                continue;
            }
            long long dp_prev[2] = {INF, INF};
            long long dp_cur[2] = {INF, INF};
            Pos exit_prev[2];
            for (size_t i = 0; i < ch.size(); ++i) {
                int u = ch[i];
                for (int cs = 0; cs < 2; ++cs) {
                    Pos entry = pos[u][cs];
                    long long base;
                    if (i == 0) {
                        base = dist(open, entry);
                    } else {
                        long long c0 = dp_prev[0] + dist(exit_prev[0], entry);
                        long long c1 = dp_prev[1] + dist(exit_prev[1], entry);
                        base = min(c0, c1);
                    }
                    dp_cur[cs] = base + cost[u][cs];
                }
                dp_prev[0] = dp_cur[0];
                dp_prev[1] = dp_cur[1];
                exit_prev[0] = pos[u][1];
                exit_prev[1] = pos[u][0];
            }
            long long total0 = dp_prev[0] + dist(exit_prev[0], close);
            long long total1 = dp_prev[1] + dist(exit_prev[1], close);
            cost[v][s] = min(total0, total1);
        }
    }

    const auto &rch = tr.children[root];
    if (rch.empty()) return 0;

    long long dp_prev[2] = {INF, INF};
    long long dp_cur[2] = {INF, INF};
    Pos exit_prev[2];
    Pos start{0, 0};
    for (size_t i = 0; i < rch.size(); ++i) {
        int u = rch[i];
        for (int cs = 0; cs < 2; ++cs) {
            Pos entry = pos[u][cs];
            long long base;
            if (i == 0) {
                base = dist(start, entry);
            } else {
                long long c0 = dp_prev[0] + dist(exit_prev[0], entry);
                long long c1 = dp_prev[1] + dist(exit_prev[1], entry);
                base = min(c0, c1);
            }
            dp_cur[cs] = base + cost[u][cs];
        }
        dp_prev[0] = dp_cur[0];
        dp_prev[1] = dp_cur[1];
        exit_prev[0] = pos[u][1];
        exit_prev[1] = pos[u][0];
    }
    return min(dp_prev[0], dp_prev[1]);
}

static void recompute_node(const Tree &tr, const vector<array<Pos, 2>> &pos,
                           vector<array<long long, 2>> &cost, int v) {
    const long long INF = (1LL << 60);
    const auto &ch = tr.children[v];
    for (int s = 0; s < 2; ++s) {
        Pos open = pos[v][s];
        Pos close = pos[v][1 - s];
        if (ch.empty()) {
            cost[v][s] = dist(open, close);
            continue;
        }
        long long dp_prev[2] = {INF, INF};
        long long dp_cur[2] = {INF, INF};
        Pos exit_prev[2];
        for (size_t i = 0; i < ch.size(); ++i) {
            int u = ch[i];
            for (int cs = 0; cs < 2; ++cs) {
                Pos entry = pos[u][cs];
                long long base;
                if (i == 0) {
                    base = dist(open, entry);
                } else {
                    long long c0 = dp_prev[0] + dist(exit_prev[0], entry);
                    long long c1 = dp_prev[1] + dist(exit_prev[1], entry);
                    base = min(c0, c1);
                }
                dp_cur[cs] = base + cost[u][cs];
            }
            dp_prev[0] = dp_cur[0];
            dp_prev[1] = dp_cur[1];
            exit_prev[0] = pos[u][1];
            exit_prev[1] = pos[u][0];
        }
        long long total0 = dp_prev[0] + dist(exit_prev[0], close);
        long long total1 = dp_prev[1] + dist(exit_prev[1], close);
        cost[v][s] = min(total0, total1);
    }
}

static int recompute_path(const Tree &tr, const vector<array<Pos, 2>> &pos,
                          vector<array<long long, 2>> &cost, int v) {
    int last = -1;
    while (v != tr.root) {
        last = v;
        recompute_node(tr, pos, cost, v);
        v = tr.parent[v];
    }
    return last;
}

static int depth_of(const Tree &tr, int v) {
    int d = 0;
    while (v != tr.root) {
        v = tr.parent[v];
        ++d;
    }
    return d;
}

static void update_subtree_sizes(vector<int> &subtree_size, const Tree &tr, int v, int delta) {
    while (v != tr.root) {
        subtree_size[v] += delta;
        v = tr.parent[v];
    }
}

static bool is_descendant(const Tree &tr, int node, int potential_parent) {
    int cur = potential_parent;
    while (cur != tr.root) {
        if (cur == node) return true;
        cur = tr.parent[cur];
    }
    return false;
}

static int choose_insert_pos(const Tree &tr, int parent, int v, XorShift64 &rng) {
    const auto &ch = tr.children[parent];
    int sz = (int)ch.size();
    if (sz == 0) return 0;
    auto center_of = [&](int node) -> Pos {
        return (node == tr.root) ? root_center2 : center2[node];
    };
    auto delta_at = [&](int pos) -> int {
        Pos left = center_of(pos > 0 ? ch[pos - 1] : parent);
        Pos right = center_of(pos < sz ? ch[pos] : parent);
        return dist(left, center2[v]) + dist(center2[v], right) - dist(left, right);
    };
    int best_pos = 0;
    int best_delta = delta_at(0);
    int end_delta = delta_at(sz);
    if (end_delta < best_delta) {
        best_delta = end_delta;
        best_pos = sz;
    }
    int samples = min(sz, 8);
    for (int t = 0; t < samples; ++t) {
        int idx = rng.next_int(0, sz - 1);
        int d0 = delta_at(idx);
        if (d0 < best_delta) {
            best_delta = d0;
            best_pos = idx;
        }
        int d1 = delta_at(idx + 1);
        if (d1 < best_delta) {
            best_delta = d1;
            best_pos = idx + 1;
        }
    }
    return best_pos;
}

static bool apply_random_move(Tree &tr, XorShift64 &rng, Move &mv,
                              vector<array<Pos, 2>> &pos) {
    int M = tr.M;
    int roll = rng.next_int(0, 99);
    const int LABEL_SWAP_RATE = 80;
    const int PARENT_SWAP_RATE = 80;

    if (roll < 40) { // swap siblings or labels
        if (rng.next_int(0, 99) < LABEL_SWAP_RATE) {
            for (int tries = 0; tries < 8; ++tries) {
                int a = rng.next_int(0, M - 1);
                int best = -1;
                int best_d = INT_MAX;
                for (int t = 0; t < 16; ++t) {
                    int b = rng.next_int(0, M - 1);
                    if (b == a) continue;
                    int d = dist(center2[a], center2[b]);
                    if (d < best_d) {
                        best_d = d;
                        best = b;
                    }
                }
                if (best == -1) continue;
                int b = best;
                swap(pos[a], pos[b]);
                swap(center2[a], center2[b]);
                mv.type = MOVE_LABEL_SWAP;
                mv.p = a;
                mv.v = b;
                return true;
            }
        }
        for (int tries = 0; tries < 8; ++tries) {
            int p = rng.next_int(0, M); // include root
            if (tr.children[p].size() < 2) continue;
            int n = (int)tr.children[p].size();
            int i = rng.next_int(0, n - 1);
            int j = rng.next_int(0, n - 1);
            if (i == j) continue;
            swap(tr.children[p][i], tr.children[p][j]);
            mv.type = MOVE_SWAP;
            mv.p = p;
            mv.i = i;
            mv.j = j;
            return true;
        }
        return false;
    }

    if (rng.next_int(0, 99) < PARENT_SWAP_RATE) { // swap parents
        for (int tries = 0; tries < 8; ++tries) {
            int a = rng.next_int(0, M - 1);
            int b = rng.next_int(0, M - 1);
            if (a == b) continue;
            if (is_descendant(tr, a, b) || is_descendant(tr, b, a)) continue;
            int pa = tr.parent[a];
            int pb = tr.parent[b];
            if (pa == pb) continue;
            auto &cha = tr.children[pa];
            auto &chb = tr.children[pb];
            int idx_a = -1;
            int idx_b = -1;
            for (int i = 0; i < (int)cha.size(); ++i) {
                if (cha[i] == a) {
                    idx_a = i;
                    break;
                }
            }
            for (int i = 0; i < (int)chb.size(); ++i) {
                if (chb[i] == b) {
                    idx_b = i;
                    break;
                }
            }
            if (idx_a == -1 || idx_b == -1) continue;
            cha[idx_a] = b;
            chb[idx_b] = a;
            tr.parent[a] = pb;
            tr.parent[b] = pa;
            mv.type = MOVE_PARENT_SWAP;
            mv.v = a;
            mv.p = b;
            mv.old_parent = pa;
            mv.new_parent = pb;
            mv.i = idx_a;
            mv.j = idx_b;
            return true;
        }
    }

    // move subtree
    int v = rng.next_int(0, M - 1);
    int old_parent = tr.parent[v];
    int best_parent = -1;
    int best_dist = INT_MAX;
    for (int tries = 0; tries < 12; ++tries) {
        int cand = rng.next_int(0, M);
        if (cand == v || cand == old_parent) continue;
        if (is_descendant(tr, v, cand)) continue;
        Pos ca = center2[v];
        Pos cb = (cand == tr.root) ? root_center2 : center2[cand];
        int d = abs(ca.r - cb.r) + abs(ca.c - cb.c);
        if (d < best_dist) {
            best_dist = d;
            best_parent = cand;
        }
    }
    if (best_parent == -1) return false;
    int new_parent = best_parent;
    auto &old_ch = tr.children[old_parent];
    int idx = -1;
    for (int i = 0; i < (int)old_ch.size(); ++i) {
        if (old_ch[i] == v) {
            idx = i;
            break;
        }
    }
    if (idx == -1) return false;

    old_ch.erase(old_ch.begin() + idx);
    auto &new_ch = tr.children[new_parent];
    int ins = choose_insert_pos(tr, new_parent, v, rng);
    new_ch.insert(new_ch.begin() + ins, v);
    tr.parent[v] = new_parent;
    mv.type = MOVE_REPARENT;
    mv.v = v;
    mv.old_parent = old_parent;
    mv.old_index = idx;
    mv.new_parent = new_parent;
    mv.new_index = ins;
    return true;
}

static void undo_move(Tree &tr, const Move &mv, vector<array<Pos, 2>> &pos) {
    if (mv.type == MOVE_SWAP) {
        swap(tr.children[mv.p][mv.i], tr.children[mv.p][mv.j]);
        return;
    }
    if (mv.type == MOVE_REPARENT) {
        auto &new_ch = tr.children[mv.new_parent];
        new_ch.erase(new_ch.begin() + mv.new_index);
        auto &old_ch = tr.children[mv.old_parent];
        old_ch.insert(old_ch.begin() + mv.old_index, mv.v);
        tr.parent[mv.v] = mv.old_parent;
        return;
    }
    if (mv.type == MOVE_LABEL_SWAP) {
        swap(pos[mv.p], pos[mv.v]);
        swap(center2[mv.p], center2[mv.v]);
        return;
    }
    if (mv.type == MOVE_PARENT_SWAP) {
        int a = mv.v;
        int b = mv.p;
        int pa = mv.old_parent;
        int pb = mv.new_parent;
        tr.children[pa][mv.i] = a;
        tr.children[pb][mv.j] = b;
        tr.parent[a] = pa;
        tr.parent[b] = pb;
    }
}

struct NodeTrace {
    vector<array<int, 2>> prev_state[2];
    int last_state[2];
};

struct RootTrace {
    vector<array<int, 2>> prev_state;
    int last_state;
};

static long long compute_trace(const Tree &tr, const vector<array<Pos, 2>> &pos,
                               vector<array<long long, 2>> &cost,
                               vector<NodeTrace> &trace,
                               RootTrace &rtrace) {
    int M = tr.M;
    int root = tr.root;
    cost.assign(M, {0LL, 0LL});
    trace.assign(M, NodeTrace());

    vector<int> post;
    post.reserve(M);
    function<void(int)> dfs = [&](int v) {
        for (int u : tr.children[v]) dfs(u);
        if (v != root) post.push_back(v);
    };
    dfs(root);

    for (int v : post) {
        const auto &ch = tr.children[v];
        for (int s = 0; s < 2; ++s) {
            Pos open = pos[v][s];
            Pos close = pos[v][1 - s];
            trace[v].prev_state[s].clear();
            trace[v].prev_state[s].resize(ch.size());
            if (ch.empty()) {
                cost[v][s] = dist(open, close);
                trace[v].last_state[s] = -1;
                continue;
            }
            long long dp_prev[2] = {INF, INF};
            long long dp_cur[2] = {INF, INF};
            Pos exit_prev[2];
            for (size_t i = 0; i < ch.size(); ++i) {
                int u = ch[i];
                for (int cs = 0; cs < 2; ++cs) {
                    Pos entry = pos[u][cs];
                    long long base;
                    int arg = -1;
                    if (i == 0) {
                        base = dist(open, entry);
                    } else {
                        long long c0 = dp_prev[0] + dist(exit_prev[0], entry);
                        long long c1 = dp_prev[1] + dist(exit_prev[1], entry);
                        if (c0 <= c1) {
                            base = c0;
                            arg = 0;
                        } else {
                            base = c1;
                            arg = 1;
                        }
                    }
                    dp_cur[cs] = base + cost[u][cs];
                    trace[v].prev_state[s][i][cs] = arg;
                }
                dp_prev[0] = dp_cur[0];
                dp_prev[1] = dp_cur[1];
                exit_prev[0] = pos[u][1];
                exit_prev[1] = pos[u][0];
            }
            long long total0 = dp_prev[0] + dist(exit_prev[0], close);
            long long total1 = dp_prev[1] + dist(exit_prev[1], close);
            if (total0 <= total1) {
                cost[v][s] = total0;
                trace[v].last_state[s] = 0;
            } else {
                cost[v][s] = total1;
                trace[v].last_state[s] = 1;
            }
        }
    }

    const auto &rch = tr.children[root];
    rtrace.prev_state.clear();
    rtrace.prev_state.resize(rch.size());
    if (rch.empty()) {
        rtrace.last_state = -1;
        return 0;
    }

    long long dp_prev[2] = {INF, INF};
    long long dp_cur[2] = {INF, INF};
    Pos exit_prev[2];
    Pos start{0, 0};
    for (size_t i = 0; i < rch.size(); ++i) {
        int u = rch[i];
        for (int cs = 0; cs < 2; ++cs) {
            Pos entry = pos[u][cs];
            long long base;
            int arg = -1;
            if (i == 0) {
                base = dist(start, entry);
            } else {
                long long c0 = dp_prev[0] + dist(exit_prev[0], entry);
                long long c1 = dp_prev[1] + dist(exit_prev[1], entry);
                if (c0 <= c1) {
                    base = c0;
                    arg = 0;
                } else {
                    base = c1;
                    arg = 1;
                }
            }
            dp_cur[cs] = base + cost[u][cs];
            rtrace.prev_state[i][cs] = arg;
        }
        dp_prev[0] = dp_cur[0];
        dp_prev[1] = dp_cur[1];
        exit_prev[0] = pos[u][1];
        exit_prev[1] = pos[u][0];
    }
    if (dp_prev[0] <= dp_prev[1]) {
        rtrace.last_state = 0;
        return dp_prev[0];
    }
    rtrace.last_state = 1;
    return dp_prev[1];
}

static void move_to(Pos &cur, const Pos &target, string &ops) {
    while (cur.r < target.r) {
        ops.push_back('D');
        cur.r++;
    }
    while (cur.r > target.r) {
        ops.push_back('U');
        cur.r--;
    }
    while (cur.c < target.c) {
        ops.push_back('R');
        cur.c++;
    }
    while (cur.c > target.c) {
        ops.push_back('L');
        cur.c--;
    }
}

static string build_solution(const Tree &tr, const vector<array<Pos, 2>> &pos) {
    int M = tr.M;
    int root = tr.root;

    vector<array<long long, 2>> cost;
    vector<NodeTrace> trace;
    RootTrace rtrace;
    compute_trace(tr, pos, cost, trace, rtrace);

    vector<int> orient(M, 0);
    const auto &rch = tr.children[root];
    if (!rch.empty()) {
        int state = rtrace.last_state;
        for (int i = (int)rch.size() - 1; i >= 0; --i) {
            int child = rch[i];
            orient[child] = state;
            int prev = rtrace.prev_state[i][state];
            state = prev;
        }

        function<void(int, int)> assign_orient = [&](int v, int s) {
            const auto &ch = tr.children[v];
            if (ch.empty()) return;
            int k = (int)ch.size();
            int st = trace[v].last_state[s];
            for (int i = k - 1; i >= 0; --i) {
                int child = ch[i];
                orient[child] = st;
                int prev = trace[v].prev_state[s][i][st];
                st = prev;
            }
            for (int child : ch) assign_orient(child, orient[child]);
        };

        for (int child : rch) assign_orient(child, orient[child]);
    }

    string ops;
    ops.reserve(10000);
    Pos cur{0, 0};

    function<void(int)> dfs_emit = [&](int v) {
        int s = orient[v];
        Pos open = pos[v][s];
        move_to(cur, open, ops);
        ops.push_back('Z');
        for (int u : tr.children[v]) dfs_emit(u);
        Pos close = pos[v][1 - s];
        move_to(cur, close, ops);
        ops.push_back('Z');
    };

    for (int child : rch) dfs_emit(child);
    return ops;
}

int main(int argc, char **argv) {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    bool log_stats = (argc > 1);

    int N;
    if (!(cin >> N)) return 0;
    int M = (N * N) / 2;

    vector<array<Pos, 2>> pos(M);
    vector<int> cnt(M, 0);
    for (int r = 0; r < N; ++r) {
        for (int c = 0; c < N; ++c) {
            int v;
            cin >> v;
            if (v < 0 || v >= M) continue;
            int k = cnt[v]++;
            if (k < 2) pos[v][k] = {r, c};
        }
    }
    center2.assign(M, {});
    for (int i = 0; i < M; ++i) {
        center2[i] = {pos[i][0].r + pos[i][1].r, pos[i][0].c + pos[i][1].c};
    }
    root_center2 = {N - 1, N - 1};

    Tree cur = build_initial_tree(M, pos);
    Tree best = cur;
    vector<array<Pos, 2>> best_pos = pos;

    vector<array<long long, 2>> cost;
    long long cur_cost = evaluate_tree(cur, pos, cost);
    long long best_cost = cur_cost;
    RootSegTree root_seg;
    root_seg.build(cur.children[cur.root], pos, cost);
    vector<int> subtree_size(M, 1);
    long long sum_depth = 0;
    {
        vector<int> depth_tmp(M, 0);
        vector<int> order;
        order.reserve(M);
        vector<int> stack;
        stack.push_back(cur.root);
        while (!stack.empty()) {
            int v = stack.back();
            stack.pop_back();
            for (int c : cur.children[v]) {
                depth_tmp[c] = (v == cur.root) ? 1 : depth_tmp[v] + 1;
                sum_depth += depth_tmp[c];
                order.push_back(c);
                stack.push_back(c);
            }
        }
        for (int i = (int)order.size() - 1; i >= 0; --i) {
            int v = order[i];
            for (int c : cur.children[v]) subtree_size[v] += subtree_size[c];
        }
    }

    Timer timer;
    XorShift64 rng;
    const double TIME_LIMIT = 1.995;
    const double T0 = 1.5;
    const double T1 = 0.4;
    const double DEPTH_WEIGHT = 0.05;
    double cur_obj = cur_cost + DEPTH_WEIGHT * (double)sum_depth;

    long long total_iters = 0;
    long long valid_moves = 0;
    long long accepted_moves = 0;
    long long improved_moves = 0;
    array<long long, 4> type_valid{};
    array<long long, 4> type_accepted{};
    array<long long, 4> type_improved{};
    while (timer.sec() < TIME_LIMIT) {
        ++total_iters;
        Move mv;
        if (!apply_random_move(cur, rng, mv, pos)) continue;
        ++valid_moves;
        if (mv.type >= 0 && mv.type < 4) {
            ++type_valid[mv.type];
        }
        int p1 = -1;
        int p2 = -1;
        if (mv.type == MOVE_REPARENT) {
            p1 = mv.old_parent;
            p2 = mv.new_parent;
        } else if (mv.type == MOVE_LABEL_SWAP) {
            p1 = mv.p;
            p2 = mv.v;
        } else if (mv.type == MOVE_PARENT_SWAP) {
            p1 = mv.old_parent;
            p2 = mv.new_parent;
        } else {
            p1 = mv.p;
        }
        bool root_changed = false;
        if (mv.type == MOVE_SWAP) {
            root_changed = (mv.p == cur.root);
        } else if (mv.type == MOVE_REPARENT) {
            root_changed = (mv.old_parent == cur.root || mv.new_parent == cur.root);
        } else if (mv.type == MOVE_PARENT_SWAP) {
            root_changed = (mv.old_parent == cur.root || mv.new_parent == cur.root);
        }

        int top1 = -1;
        int top2 = -1;
        if (p1 != -1) top1 = recompute_path(cur, pos, cost, p1);
        if (p2 != -1 && p2 != p1) top2 = recompute_path(cur, pos, cost, p2);

        if (root_changed) {
            if (mv.type == MOVE_SWAP && mv.p == cur.root) {
                root_seg.swap_positions(mv.i, mv.j, pos, cost);
            } else {
                root_seg.build(cur.children[cur.root], pos, cost);
            }
        } else {
            if (top1 != -1) root_seg.update_child(top1, pos, cost);
            if (top2 != -1 && top2 != top1) root_seg.update_child(top2, pos, cost);
        }
        long long cand_cost = root_seg.total_cost();
        long long cand_sum_depth = sum_depth;
        int moved_size = 0;
        int size_a = 0;
        int size_b = 0;
        if (mv.type == MOVE_REPARENT) {
            int new_depth = depth_of(cur, mv.new_parent) + 1;
            int old_depth = depth_of(cur, mv.old_parent) + 1;
            int delta = new_depth - old_depth;
            moved_size = subtree_size[mv.v];
            cand_sum_depth = sum_depth + 1LL * delta * moved_size;
        } else if (mv.type == MOVE_PARENT_SWAP) {
            int pa = mv.old_parent;
            int pb = mv.new_parent;
            int delta = depth_of(cur, pb) - depth_of(cur, pa);
            size_a = subtree_size[mv.v];
            size_b = subtree_size[mv.p];
            cand_sum_depth = sum_depth + 1LL * delta * (size_a - size_b);
        }
        double cand_obj = cand_cost + DEPTH_WEIGHT * (double)cand_sum_depth;

        bool accept = false;
        if (cand_obj <= cur_obj) {
            accept = true;
        } else {
            double t = timer.sec() / TIME_LIMIT;
            double temp = T0 * pow(T1 / T0, t);
            double prob = exp((cur_obj - cand_obj) / temp);
            if (rng.next_double() < prob) accept = true;
        }

        if (accept) {
            // cerr << "Iter " << iter << ": Cost " << cand_cost << ", Best Cost " << best_cost << "\n";
            // cerr << "Best Update? : " << (cand_cost < best_cost ? "Yes" : "No") << "\n";
            cur_cost = cand_cost;
            cur_obj = cand_obj;
            ++accepted_moves;
            if (mv.type >= 0 && mv.type < 4) {
                ++type_accepted[mv.type];
            }
            if (mv.type == MOVE_REPARENT) {
                sum_depth = cand_sum_depth;
                update_subtree_sizes(subtree_size, cur, mv.old_parent, -moved_size);
                update_subtree_sizes(subtree_size, cur, mv.new_parent, +moved_size);
            } else if (mv.type == MOVE_PARENT_SWAP) {
                sum_depth = cand_sum_depth;
                int delta = size_b - size_a;
                update_subtree_sizes(subtree_size, cur, mv.old_parent, delta);
                update_subtree_sizes(subtree_size, cur, mv.new_parent, -delta);
            }
            if (cur_cost < best_cost) {
                best_cost = cur_cost;
                best = cur;
                best_pos = pos;
                ++improved_moves;
                if (mv.type >= 0 && mv.type < 4) {
                    ++type_improved[mv.type];
                }
            }
        } else {
            undo_move(cur, mv, pos);
            top1 = -1;
            top2 = -1;
            if (p1 != -1) top1 = recompute_path(cur, pos, cost, p1);
            if (p2 != -1 && p2 != p1) top2 = recompute_path(cur, pos, cost, p2);
            if (root_changed) {
                if (mv.type == MOVE_SWAP && mv.p == cur.root) {
                    root_seg.swap_positions(mv.i, mv.j, pos, cost);
                } else {
                    root_seg.build(cur.children[cur.root], pos, cost);
                }
            } else {
                if (top1 != -1) root_seg.update_child(top1, pos, cost);
                if (top2 != -1 && top2 != top1) root_seg.update_child(top2, pos, cost);
            }
            cur_cost = root_seg.total_cost();
            cur_obj = cur_cost + DEPTH_WEIGHT * (double)sum_depth;
        }
    }

    if (log_stats) {
        auto rate = [](long long a, long long b) -> double {
            if (b == 0) return 0.0;
            return 100.0 * (double)a / (double)b;
        };
        cerr << "iters=" << total_iters << " valid=" << valid_moves
             << " accepted=" << accepted_moves << " improved=" << improved_moves << "\n";
        cerr << "accept_rate=" << fixed << setprecision(2) << rate(accepted_moves, valid_moves) << "% "
             << "improve_rate=" << rate(improved_moves, valid_moves) << "%\n";
        const char *names[4] = {"swap", "reparent", "lswap", "pswap"};
        for (int t = 0; t < 4; ++t) {
            cerr << names[t] << ": valid=" << type_valid[t]
                 << " accepted=" << type_accepted[t]
                 << " improved=" << type_improved[t] << "\n";
        }
    }

    string ops = build_solution(best, best_pos);
    for (char ch : ops) {
        cout << ch << '\n';
    }
    return 0;
}

Submission Info

Submission Time
Task A - Stack to Match Pairs
User through
Language C++23 (GCC 15.2.0)
Score 2286982
Code Size 33528 Byte
Status AC
Exec Time 1996 ms
Memory 4100 KiB

Compile Error

./Main.cpp: In function 'int main(int, char**)':
./Main.cpp:791:27: warning: unused parameter 'argv' [-Wunused-parameter]
  791 | int main(int argc, char **argv) {
      |                    ~~~~~~~^~~~

Judge Result

Set Name test_ALL
Score / Max Score 2286982 / 2460000
Status
AC × 150
Set Name Test Cases
test_ALL test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt, test_0100.txt, test_0101.txt, test_0102.txt, test_0103.txt, test_0104.txt, test_0105.txt, test_0106.txt, test_0107.txt, test_0108.txt, test_0109.txt, test_0110.txt, test_0111.txt, test_0112.txt, test_0113.txt, test_0114.txt, test_0115.txt, test_0116.txt, test_0117.txt, test_0118.txt, test_0119.txt, test_0120.txt, test_0121.txt, test_0122.txt, test_0123.txt, test_0124.txt, test_0125.txt, test_0126.txt, test_0127.txt, test_0128.txt, test_0129.txt, test_0130.txt, test_0131.txt, test_0132.txt, test_0133.txt, test_0134.txt, test_0135.txt, test_0136.txt, test_0137.txt, test_0138.txt, test_0139.txt, test_0140.txt, test_0141.txt, test_0142.txt, test_0143.txt, test_0144.txt, test_0145.txt, test_0146.txt, test_0147.txt, test_0148.txt, test_0149.txt
Case Name Status Exec Time Memory
test_0000.txt AC 1996 ms 3988 KiB
test_0001.txt AC 1996 ms 3920 KiB
test_0002.txt AC 1996 ms 3904 KiB
test_0003.txt AC 1996 ms 3844 KiB
test_0004.txt AC 1996 ms 3976 KiB
test_0005.txt AC 1996 ms 3836 KiB
test_0006.txt AC 1996 ms 3844 KiB
test_0007.txt AC 1996 ms 3904 KiB
test_0008.txt AC 1996 ms 3844 KiB
test_0009.txt AC 1996 ms 3968 KiB
test_0010.txt AC 1996 ms 4100 KiB
test_0011.txt AC 1996 ms 3988 KiB
test_0012.txt AC 1996 ms 4100 KiB
test_0013.txt AC 1996 ms 3988 KiB
test_0014.txt AC 1996 ms 3920 KiB
test_0015.txt AC 1996 ms 3800 KiB
test_0016.txt AC 1996 ms 4100 KiB
test_0017.txt AC 1996 ms 3968 KiB
test_0018.txt AC 1996 ms 3888 KiB
test_0019.txt AC 1996 ms 3748 KiB
test_0020.txt AC 1996 ms 3868 KiB
test_0021.txt AC 1996 ms 3920 KiB
test_0022.txt AC 1996 ms 4100 KiB
test_0023.txt AC 1996 ms 3960 KiB
test_0024.txt AC 1996 ms 4100 KiB
test_0025.txt AC 1996 ms 3748 KiB
test_0026.txt AC 1996 ms 3960 KiB
test_0027.txt AC 1996 ms 3976 KiB
test_0028.txt AC 1996 ms 3976 KiB
test_0029.txt AC 1996 ms 3920 KiB
test_0030.txt AC 1996 ms 3904 KiB
test_0031.txt AC 1996 ms 3868 KiB
test_0032.txt AC 1996 ms 3904 KiB
test_0033.txt AC 1996 ms 3960 KiB
test_0034.txt AC 1996 ms 3904 KiB
test_0035.txt AC 1996 ms 3868 KiB
test_0036.txt AC 1996 ms 3924 KiB
test_0037.txt AC 1996 ms 4100 KiB
test_0038.txt AC 1996 ms 3968 KiB
test_0039.txt AC 1996 ms 3844 KiB
test_0040.txt AC 1996 ms 3884 KiB
test_0041.txt AC 1996 ms 3960 KiB
test_0042.txt AC 1996 ms 3988 KiB
test_0043.txt AC 1996 ms 3800 KiB
test_0044.txt AC 1996 ms 3988 KiB
test_0045.txt AC 1996 ms 4100 KiB
test_0046.txt AC 1996 ms 3904 KiB
test_0047.txt AC 1996 ms 3920 KiB
test_0048.txt AC 1996 ms 3920 KiB
test_0049.txt AC 1996 ms 3904 KiB
test_0050.txt AC 1996 ms 3888 KiB
test_0051.txt AC 1996 ms 3844 KiB
test_0052.txt AC 1996 ms 3800 KiB
test_0053.txt AC 1996 ms 3988 KiB
test_0054.txt AC 1996 ms 3960 KiB
test_0055.txt AC 1996 ms 3924 KiB
test_0056.txt AC 1996 ms 3920 KiB
test_0057.txt AC 1996 ms 3884 KiB
test_0058.txt AC 1996 ms 3920 KiB
test_0059.txt AC 1996 ms 4100 KiB
test_0060.txt AC 1996 ms 3824 KiB
test_0061.txt AC 1996 ms 3920 KiB
test_0062.txt AC 1996 ms 4100 KiB
test_0063.txt AC 1996 ms 3968 KiB
test_0064.txt AC 1996 ms 3748 KiB
test_0065.txt AC 1996 ms 3748 KiB
test_0066.txt AC 1996 ms 3904 KiB
test_0067.txt AC 1996 ms 3844 KiB
test_0068.txt AC 1996 ms 3924 KiB
test_0069.txt AC 1996 ms 3920 KiB
test_0070.txt AC 1996 ms 3844 KiB
test_0071.txt AC 1996 ms 3976 KiB
test_0072.txt AC 1996 ms 3904 KiB
test_0073.txt AC 1996 ms 3904 KiB
test_0074.txt AC 1996 ms 3920 KiB
test_0075.txt AC 1996 ms 4100 KiB
test_0076.txt AC 1996 ms 3920 KiB
test_0077.txt AC 1996 ms 3868 KiB
test_0078.txt AC 1996 ms 3748 KiB
test_0079.txt AC 1996 ms 3868 KiB
test_0080.txt AC 1996 ms 3844 KiB
test_0081.txt AC 1996 ms 3924 KiB
test_0082.txt AC 1996 ms 3920 KiB
test_0083.txt AC 1996 ms 3988 KiB
test_0084.txt AC 1996 ms 3868 KiB
test_0085.txt AC 1996 ms 3976 KiB
test_0086.txt AC 1996 ms 3888 KiB
test_0087.txt AC 1996 ms 3988 KiB
test_0088.txt AC 1996 ms 3920 KiB
test_0089.txt AC 1996 ms 3848 KiB
test_0090.txt AC 1996 ms 3968 KiB
test_0091.txt AC 1996 ms 3988 KiB
test_0092.txt AC 1996 ms 3888 KiB
test_0093.txt AC 1996 ms 3924 KiB
test_0094.txt AC 1996 ms 3888 KiB
test_0095.txt AC 1996 ms 3844 KiB
test_0096.txt AC 1996 ms 3924 KiB
test_0097.txt AC 1996 ms 3868 KiB
test_0098.txt AC 1996 ms 3800 KiB
test_0099.txt AC 1996 ms 3844 KiB
test_0100.txt AC 1996 ms 3920 KiB
test_0101.txt AC 1996 ms 3920 KiB
test_0102.txt AC 1996 ms 3904 KiB
test_0103.txt AC 1996 ms 3920 KiB
test_0104.txt AC 1996 ms 3920 KiB
test_0105.txt AC 1996 ms 3904 KiB
test_0106.txt AC 1996 ms 3968 KiB
test_0107.txt AC 1996 ms 3800 KiB
test_0108.txt AC 1996 ms 3976 KiB
test_0109.txt AC 1996 ms 3748 KiB
test_0110.txt AC 1996 ms 3844 KiB
test_0111.txt AC 1996 ms 3924 KiB
test_0112.txt AC 1996 ms 3844 KiB
test_0113.txt AC 1996 ms 3968 KiB
test_0114.txt AC 1996 ms 3868 KiB
test_0115.txt AC 1996 ms 3868 KiB
test_0116.txt AC 1996 ms 3920 KiB
test_0117.txt AC 1996 ms 3924 KiB
test_0118.txt AC 1996 ms 3960 KiB
test_0119.txt AC 1996 ms 3960 KiB
test_0120.txt AC 1996 ms 4080 KiB
test_0121.txt AC 1996 ms 3800 KiB
test_0122.txt AC 1996 ms 3868 KiB
test_0123.txt AC 1996 ms 3976 KiB
test_0124.txt AC 1996 ms 3960 KiB
test_0125.txt AC 1996 ms 3904 KiB
test_0126.txt AC 1996 ms 3988 KiB
test_0127.txt AC 1996 ms 3904 KiB
test_0128.txt AC 1996 ms 3848 KiB
test_0129.txt AC 1996 ms 3920 KiB
test_0130.txt AC 1996 ms 3920 KiB
test_0131.txt AC 1996 ms 3888 KiB
test_0132.txt AC 1996 ms 3884 KiB
test_0133.txt AC 1996 ms 3920 KiB
test_0134.txt AC 1996 ms 3976 KiB
test_0135.txt AC 1996 ms 3904 KiB
test_0136.txt AC 1996 ms 3960 KiB
test_0137.txt AC 1996 ms 3844 KiB
test_0138.txt AC 1996 ms 3788 KiB
test_0139.txt AC 1996 ms 3904 KiB
test_0140.txt AC 1996 ms 3920 KiB
test_0141.txt AC 1996 ms 3748 KiB
test_0142.txt AC 1996 ms 3748 KiB
test_0143.txt AC 1996 ms 3892 KiB
test_0144.txt AC 1996 ms 3748 KiB
test_0145.txt AC 1996 ms 3920 KiB
test_0146.txt AC 1996 ms 3748 KiB
test_0147.txt AC 1996 ms 4100 KiB
test_0148.txt AC 1996 ms 3988 KiB
test_0149.txt AC 1996 ms 3880 KiB