提出 #76877085


ソースコード 拡げる

#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <functional>
#include <iostream>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;

struct Candidate {
    int level = 0;
    vector<int> path;
    vector<int> edges;
    vector<int> seals;
    int bypass_length = 0;
    int score = 0;
};

struct DoorOut {
    int d, i, j, type;
};

struct SwitchOut {
    int i, j, type;
};

class Solver {
    using Clock = chrono::steady_clock;
    int N, M, K, V;
    vector<string> board;
    vector<vector<int>> adj;
    vector<int> vertices;
    vector<int> all_edges;
    vector<vector<int>> corridors;
    vector<vector<int>> cycles;
    vector<vector<Candidate>> candidates;
    bool debug;
    int start = 0, goal;
    long long search_nodes = 0;
    long long search_limit = 2000;
    Clock::time_point started_at = Clock::now();
    static constexpr double TIME_LIMIT_SEC = 1.55;

    bool time_up() const {
        return chrono::duration<double>(Clock::now() - started_at).count() >=
               TIME_LIMIT_SEC;
    }

    int edge_id(int a, int b) const {
        if (a > b) swap(a, b);
        return a * V + b;
    }
    pair<int, int> edge_vertices(int id) const {
        return {id / V, id % V};
    }
    bool contains_sorted(const vector<int>& a, int x) const {
        return binary_search(a.begin(), a.end(), x);
    }
    static void sort_unique(vector<int>& a) {
        sort(a.begin(), a.end());
        a.erase(unique(a.begin(), a.end()), a.end());
    }
    static bool intersects(const vector<int>& a, const vector<int>& b) {
        size_t i = 0, j = 0;
        while (i < a.size() && j < b.size()) {
            if (a[i] == b[j]) return true;
            if (a[i] < b[j]) ++i;
            else ++j;
        }
        return false;
    }
    static vector<int> united(const vector<int>& a, const vector<int>& b) {
        vector<int> r;
        r.reserve(a.size() + b.size());
        set_union(a.begin(), a.end(), b.begin(), b.end(), back_inserter(r));
        return r;
    }
    vector<char> component(const vector<int>& blocked, int source = 0) const {
        vector<char> seen(V, false);
        queue<int> q;
        seen[source] = true;
        q.push(source);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (int w : adj[v]) {
                if (seen[w] || contains_sorted(blocked, edge_id(v, w))) continue;
                seen[w] = true;
                q.push(w);
            }
        }
        return seen;
    }
    int shortest_without_edges(int source, int target, const vector<int>& blocked) const {
        vector<int> dist(V, -1);
        queue<int> q;
        dist[source] = 0;
        q.push(source);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            if (v == target) return dist[v];
            for (int w : adj[v]) {
                if (dist[w] >= 0 || contains_sorted(blocked, edge_id(v, w))) continue;
                dist[w] = dist[v] + 1;
                q.push(w);
            }
        }
        return -1;
    }
    vector<int> distances(int source, const vector<int>& blocked = {}) const {
        vector<int> dist(V, -1);
        queue<int> q;
        dist[source] = 0;
        q.push(source);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (int w : adj[v]) {
                if (dist[w] >= 0 || contains_sorted(blocked, edge_id(v, w))) continue;
                dist[w] = dist[v] + 1;
                q.push(w);
            }
        }
        return dist;
    }

    vector<vector<int>> list_corridors() const {
        vector<char> terminal(V, false);
        for (int v : vertices) {
            terminal[v] = (adj[v].size() != 2 || v == start || v == goal);
        }
        unordered_set<int> used;
        vector<vector<int>> result;
        for (int begin : vertices) if (terminal[begin]) {
            for (int first : adj[begin]) {
                int e = edge_id(begin, first);
                if (used.count(e)) continue;
                used.insert(e);
                vector<int> path{begin, first};
                int prev = begin, cur = first;
                while (!terminal[cur]) {
                    int nxt = adj[cur][0] == prev ? adj[cur][1] : adj[cur][0];
                    used.insert(edge_id(cur, nxt));
                    path.push_back(nxt);
                    prev = cur;
                    cur = nxt;
                }
                result.push_back(move(path));
            }
        }
        return result;
    }

    string cycle_key(const vector<int>& path) const {
        vector<int> es;
        es.reserve(path.size());
        for (int i = 0; i < (int)path.size(); ++i) {
            es.push_back(edge_id(path[i], path[(i + 1) % path.size()]));
        }
        sort(es.begin(), es.end());
        string key;
        key.reserve(es.size() * 5);
        for (int e : es) {
            key += to_string(e);
            key.push_back(',');
        }
        return key;
    }
    void add_cycle(vector<vector<int>>& result, unordered_set<string>& keys,
                   const vector<int>& path) const {
        if (path.size() < 4) return;
        unordered_set<int> vs(path.begin(), path.end());
        if (vs.size() != path.size()) return;
        string key = cycle_key(path);
        if (keys.insert(key).second) result.push_back(path);
    }
    void add_fundamental_cycles(vector<vector<int>>& result, unordered_set<string>& keys,
                                const vector<int>& parent, const vector<int>& depth) const {
        for (int eid : all_edges) {
            auto [u0, v0] = edge_vertices(eid);
            if (parent[u0] == v0 || parent[v0] == u0) continue;
            int a = u0, b = v0;
            vector<int> left{a}, right{b};
            while (depth[a] > depth[b]) {
                a = parent[a];
                left.push_back(a);
            }
            while (depth[b] > depth[a]) {
                b = parent[b];
                right.push_back(b);
            }
            while (a != b) {
                a = parent[a];
                b = parent[b];
                left.push_back(a);
                right.push_back(b);
            }
            vector<int> path = left;
            for (int i = (int)right.size() - 2; i >= 0; --i) path.push_back(right[i]);
            add_cycle(result, keys, path);
        }
    }
    vector<vector<int>> list_cycles() const {
        vector<vector<int>> result;
        unordered_set<string> keys;
        uint64_t seed = 0;
        for (int i = 0; i < N; ++i) for (char c : board[i]) {
            seed = seed * 1000003ULL + (uint64_t)(i + 1) * (unsigned char)c;
        }
        mt19937_64 rng(seed);
        // C++ではPython版より多くのDFS順を安価に試せる。
        for (int trial = 0; trial < 8; ++trial) {
            vector<int> parent(V, -2), depth(V, -1), order_index(V, 0);
            vector<vector<int>> shuffled = adj;
            for (int v : vertices) shuffle(shuffled[v].begin(), shuffled[v].end(), rng);
            parent[start] = -1;
            depth[start] = 0;
            vector<int> stack{start};
            while (!stack.empty()) {
                int v = stack.back();
                if (order_index[v] == (int)shuffled[v].size()) {
                    stack.pop_back();
                    continue;
                }
                int w = shuffled[v][order_index[v]++];
                if (parent[w] != -2) continue;
                parent[w] = v;
                depth[w] = depth[v] + 1;
                stack.push_back(w);
            }
            add_fundamental_cycles(result, keys, parent, depth);
        }
        // 各辺を除いた最短迂回路も残す。
        for (int removed : all_edges) {
            auto [s, t] = edge_vertices(removed);
            vector<int> prev(V, -2);
            queue<int> q;
            prev[s] = -1;
            q.push(s);
            while (!q.empty() && prev[t] == -2) {
                int v = q.front();
                q.pop();
                for (int w : adj[v]) {
                    if (edge_id(v, w) == removed || prev[w] != -2) continue;
                    prev[w] = v;
                    q.push(w);
                }
            }
            if (prev[t] == -2) continue;
            vector<int> path;
            for (int cur = t; cur >= 0; cur = prev[cur]) path.push_back(cur);
            reverse(path.begin(), path.end());
            add_cycle(result, keys, path);
        }
        return result;
    }

    vector<int> minimum_path_seals(const vector<int>& path,
                                   const vector<int>& path_edges) const {
        vector<int> index(V, -1);
        vector<char> in_path(V, false);
        for (int i = 0; i < (int)path.size(); ++i) {
            index[path[i]] = i;
            in_path[path[i]] = true;
        }
        unordered_set<int> seal_set;
        for (int x = 0; x < (int)path.size(); ++x) {
            int v = path[x];
            for (int w : adj[v]) {
                int y = index[w], e = edge_id(v, w);
                if (y < 0 || contains_sorted(path_edges, e)) continue;
                if ((x == 0 && y == (int)path.size() - 1) ||
                    (y == 0 && x == (int)path.size() - 1)) continue;
                seal_set.insert(e);
            }
        }
        vector<char> seen(V, false);
        for (int root : vertices) {
            if (in_path[root] || seen[root]) continue;
            vector<int> comp;
            queue<int> q;
            q.push(root);
            seen[root] = true;
            while (!q.empty()) {
                int v = q.front();
                q.pop();
                comp.push_back(v);
                for (int w : adj[v]) {
                    if (in_path[w] || seen[w]) continue;
                    seen[w] = true;
                    q.push(w);
                }
            }
            vector<vector<int>> attachments(path.size());
            bool has_start = false, has_goal = false;
            for (int v : comp) {
                has_start |= (v == start);
                has_goal |= (v == goal);
                for (int w : adj[v]) if (index[w] >= 0) {
                    attachments[index[w]].push_back(edge_id(v, w));
                }
            }
            vector<int> attached_indices;
            for (int i = 0; i < (int)attachments.size(); ++i) {
                if (!attachments[i].empty()) attached_indices.push_back(i);
            }
            if (attached_indices.empty()) continue;
            vector<char> allowed(path.size(), false);
            bool endpoint_attached = !attachments.front().empty() || !attachments.back().empty();
            if (has_start || has_goal || endpoint_attached) {
                if (!attachments.front().empty()) allowed[0] = true;
                if (!attachments.back().empty()) allowed[path.size() - 1] = true;
            } else {
                int best = attached_indices[0];
                for (int x : attached_indices) {
                    if (attachments[x].size() > attachments[best].size()) best = x;
                }
                allowed[best] = true;
            }
            for (int x : attached_indices) if (!allowed[x]) {
                for (int e : attachments[x]) seal_set.insert(e);
            }
        }
        vector<int> seals(seal_set.begin(), seal_set.end());
        sort(seals.begin(), seals.end());
        return seals;
    }

    vector<Candidate> enumerate_candidates(int level) const {
        int required = 3 * level + 2;
        vector<int> start_dist = distances(start);
        unordered_map<string, pair<vector<int>, int>> raw;
        auto add_raw = [&](const vector<int>& path, int remainder) {
            string key;
            key.reserve(path.size() * 4);
            for (int v : path) {
                key += to_string(v);
                key.push_back(',');
            }
            auto it = raw.find(key);
            if (it == raw.end() || remainder > it->second.second) {
                raw[key] = {path, remainder};
            }
        };
        for (const auto& corridor : corridors) {
            int ec = (int)corridor.size() - 1;
            if (ec < required) continue;
            vector<int> lengths{required, min(ec, required + 4),
                                min(ec, required + 8), ec};
            sort_unique(lengths);
            for (int len : lengths) for (int off = 0; off + len <= ec; ++off) {
                vector<int> path(corridor.begin() + off, corridor.begin() + off + len + 1);
                add_raw(path, 1000);
                reverse(path.begin(), path.end());
                add_raw(path, 1000);
            }
        }
        for (const auto& cyc : cycles) {
            int cs = cyc.size();
            if (cs <= required) continue;
            int mx = cs - 1;
            vector<int> lengths{required, min(mx, required + 4), min(mx, required + 8),
                                max(required, cs / 3), max(required, cs / 2),
                                max(required, cs * 2 / 3), mx};
            sort_unique(lengths);
            for (int len : lengths) for (int begin = 0; begin < cs; ++begin) {
                for (int dir : {-1, 1}) {
                    vector<int> path;
                    path.reserve(len + 1);
                    for (int x = 0; x <= len; ++x) {
                        int p = (begin + dir * x) % cs;
                        if (p < 0) p += cs;
                        path.push_back(cyc[p]);
                    }
                    add_raw(path, cs - len);
                }
            }
        }
        vector<pair<vector<int>, int>> ranked;
        ranked.reserve(raw.size());
        for (auto& [_, value] : raw) ranked.push_back(move(value));
        sort(ranked.begin(), ranked.end(), [&](const auto& a, const auto& b) {
            auto value = [&](const auto& x) {
                const auto& p = x.first;
                return x.second * 16 + ((int)p.size() - 1) * 4 +
                       min(start_dist[p.front()], start_dist[p.back()]);
            };
            return value(a) > value(b);
        });
        vector<Candidate> result;
        int inspect = min<int>(ranked.size(), 1500);
        for (int ri = 0; ri < inspect; ++ri) {
            const vector<int>& path = ranked[ri].first;
            if (find(path.begin() + 1, path.end() - 1, start) != path.end() - 1 ||
                find(path.begin() + 1, path.end() - 1, goal) != path.end() - 1) continue;
            vector<int> path_edges;
            for (int i = 0; i + 1 < (int)path.size(); ++i) {
                path_edges.push_back(edge_id(path[i], path[i + 1]));
            }
            sort_unique(path_edges);
            vector<int> seals = minimum_path_seals(path, path_edges);
            if ((int)seals.size() > M - 2 * (level + 1)) continue;
            vector<int> blocked = united(path_edges, seals);
            vector<char> comp = component(blocked);
            if (!comp[goal] || !comp[path.front()] || !comp[path.back()]) continue;
            int bypass = shortest_without_edges(path.front(), path.back(), blocked);
            if (bypass < 0) continue;
            int remoteness = min(start_dist[path.front()], start_dist[path.back()]);
            int score = bypass * 16 + remoteness +
                        ((int)path.size() - required - 1) * 4 -
                        (int)seals.size() * 80;
            result.push_back(Candidate{level, path, path_edges, seals, bypass, score});
        }
        vector<int> ids(result.size());
        iota(ids.begin(), ids.end(), 0);
        vector<vector<int>> orders;
        auto make_order = [&](auto cmp) {
            vector<int> o = ids;
            sort(o.begin(), o.end(), cmp);
            orders.push_back(move(o));
        };
        make_order([&](int a, int b) { return result[a].score > result[b].score; });
        make_order([&](int a, int b) {
            return tuple(-int(result[a].seals.size()), result[a].bypass_length,
                         int(result[a].path.size())) >
                   tuple(-int(result[b].seals.size()), result[b].bypass_length,
                         int(result[b].path.size()));
        });
        make_order([&](int a, int b) {
            return tuple(result[a].bypass_length, -int(result[a].seals.size()), result[a].score) >
                   tuple(result[b].bypass_length, -int(result[b].seals.size()), result[b].score);
        });
        make_order([&](int a, int b) {
            return tuple(int(result[a].path.size()), -int(result[a].seals.size()), result[a].score) >
                   tuple(int(result[b].path.size()), -int(result[b].seals.size()), result[b].score);
        });
        vector<Candidate> picked;
        unordered_set<string> used;
        for (const auto& order : orders) {
            int take = min<int>(50, order.size());
            for (int z = 0; z < take; ++z) {
                const Candidate& c = result[order[z]];
                string key;
                for (int v : c.path) key += to_string(v) + ",";
                if (used.insert(key).second) picked.push_back(c);
            }
        }
        sort(picked.begin(), picked.end(),
             [](const Candidate& a, const Candidate& b) { return a.score > b.score; });
        if (picked.size() > 160) picked.resize(160);
        return picked;
    }

    vector<int> sg_bridges(const vector<int>& blocked) const {
        vector<char> comp = component(blocked);
        if (!comp[goal]) return {};
        vector<int> tin(V, -1), low(V, -1), parent(V, -1);
        unordered_set<int> bridge_set;
        int timer = 0;
        function<void(int, int)> dfs = [&](int v, int p) {
            tin[v] = low[v] = timer++;
            for (int w : adj[v]) {
                int e = edge_id(v, w);
                if (!comp[w] || contains_sorted(blocked, e) || w == p) continue;
                if (tin[w] >= 0) low[v] = min(low[v], tin[w]);
                else {
                    parent[w] = v;
                    dfs(w, v);
                    low[v] = min(low[v], low[w]);
                    if (low[w] > tin[v]) bridge_set.insert(e);
                }
            }
        };
        dfs(start, -1);
        vector<int> result;
        int cur = goal;
        while (cur != start) {
            if (parent[cur] < 0) return {};
            int e = edge_id(cur, parent[cur]);
            if (bridge_set.count(e)) result.push_back(e);
            cur = parent[cur];
        }
        reverse(result.begin(), result.end());
        return result;
    }
    vector<int> final_gate_edges(const vector<int>& blocked,
                                 const vector<int>& endpoints, int needed) const {
        vector<int> eligible;
        for (int bridge : sg_bridges(blocked)) {
            vector<int> cut = blocked;
            auto it = lower_bound(cut.begin(), cut.end(), bridge);
            cut.insert(it, bridge);
            vector<char> side = component(cut);
            if (side[goal]) continue;
            bool ok = true;
            for (int v : endpoints) ok &= side[v];
            if (ok) eligible.push_back(bridge);
        }
        if ((int)eligible.size() < needed) return {};
        return vector<int>(eligible.end() - needed, eligible.end());
    }

    bool common_component_ok(const vector<const Candidate*>& selected) const {
        vector<int> blocked, endpoints{start, goal};
        for (const Candidate* c : selected) {
            blocked.insert(blocked.end(), c->edges.begin(), c->edges.end());
            blocked.insert(blocked.end(), c->seals.begin(), c->seals.end());
            endpoints.push_back(c->path.front());
            endpoints.push_back(c->path.back());
        }
        sort_unique(blocked);
        vector<char> comp = component(blocked);
        for (int v : endpoints) if (!comp[v]) return false;
        return true;
    }
    bool try_select(const vector<int>& levels, int at,
                    vector<const Candidate*>& selected,
                    vector<const Candidate*>& answer,
                    vector<int>& answer_final) {
        if (++search_nodes > search_limit) return false;
        if (at == (int)levels.size()) {
            if (!common_component_ok(selected)) return false;
            vector<int> blocked, endpoints;
            int gadget_doors = 0;
            for (const Candidate* c : selected) {
                blocked.insert(blocked.end(), c->edges.begin(), c->edges.end());
                blocked.insert(blocked.end(), c->seals.begin(), c->seals.end());
                endpoints.push_back(c->path.front());
                endpoints.push_back(c->path.back());
                gadget_doors += 2 * (c->level + 1);
            }
            sort_unique(blocked);
            int needed = levels.empty() ? 1 : levels.front() + 1;
            vector<int> final_edges = final_gate_edges(blocked, endpoints, needed);
            unordered_set<int> seal_set;
            for (const Candidate* c : selected) {
                seal_set.insert(c->seals.begin(), c->seals.end());
            }
            if ((int)final_edges.size() == needed &&
                gadget_doors + (int)seal_set.size() + needed <= M) {
                answer = selected;
                answer_final = move(final_edges);
                return true;
            }
            return false;
        }
        int level = levels[at];
        vector<char> existing_internal(V, false), existing_all(V, false);
        vector<int> existing_edges, existing_seals;
        int gadget_doors = 0;
        for (const Candidate* c : selected) {
            gadget_doors += 2 * (c->level + 1);
            for (int v : c->path) existing_all[v] = true;
            for (int i = 1; i + 1 < (int)c->path.size(); ++i) existing_internal[c->path[i]] = true;
            existing_edges.insert(existing_edges.end(), c->edges.begin(), c->edges.end());
            existing_seals.insert(existing_seals.end(), c->seals.begin(), c->seals.end());
        }
        sort_unique(existing_edges);
        sort_unique(existing_seals);
        for (const Candidate& c : candidates[level]) {
            bool bad = false;
            for (int i = 1; i + 1 < (int)c.path.size(); ++i) bad |= existing_all[c.path[i]];
            bad |= existing_internal[c.path.front()] || existing_internal[c.path.back()];
            if (bad || intersects(c.edges, existing_seals) || intersects(c.seals, existing_edges)) continue;
            vector<int> all_seals = united(existing_seals, c.seals);
            int minimum_final = levels.front() + 1;
            if (gadget_doors + 2 * (c.level + 1) +
                    (int)all_seals.size() + minimum_final > M) continue;
            selected.push_back(&c);
            if (try_select(levels, at + 1, selected, answer, answer_final)) return true;
            selected.pop_back();
        }
        return false;
    }

    tuple<int, vector<const Candidate*>, vector<int>> select_layout() {
        int max_level = min(5, K - 1);
        candidates.assign(max_level + 1, {});
        for (int level = 1; level <= max_level; ++level) {
            candidates[level] = enumerate_candidates(level);
        }
        for (int level = max_level; level >= 0; --level) {
            vector<int> levels;
            for (int x = level; x >= 1; --x) levels.push_back(x);
            if (debug) {
                cerr << "target level " << level << " cycles " << cycles.size() << " candidates";
                for (int x : levels) cerr << " " << x << ":" << candidates[x].size();
                cerr << '\n';
            }
            bool missing = false;
            for (int x : levels) missing |= candidates[x].empty();
            if (missing) continue;
            search_nodes = 0;
            vector<const Candidate*> selected, answer;
            vector<int> final_edges;
            if (try_select(levels, 0, selected, answer, final_edges)) {
                if (debug) cerr << "search nodes " << search_nodes << '\n';
                return {level, answer, final_edges};
            }
            if (debug) cerr << "search nodes " << search_nodes << '\n';
        }
        vector<int> bridges = sg_bridges({});
        vector<int> fallback;
        if (!bridges.empty()) fallback.push_back(bridges.back());
        return {0, {}, fallback};
    }

    DoorOut edge_to_output(int eid, int type) const {
        auto [a, b] = edge_vertices(eid);
        int ai = a / N, aj = a % N, bi = b / N, bj = b % N;
        if (aj == bj) return {0, min(ai, bi), aj, type};
        return {1, ai, min(aj, bj), type};
    }
    pair<vector<DoorOut>, vector<SwitchOut>>
    build_gadgets(const vector<const Candidate*>& selected) const {
        vector<DoorOut> doors;
        vector<SwitchOut> switches;
        unordered_set<int> seal_set;
        for (const Candidate* c : selected) {
            int level = c->level, ec = c->path.size() - 1;
            for (int x = 0; x < level; ++x) {
                doors.push_back(edge_to_output(edge_id(c->path[x], c->path[x + 1]), 2 * x + 1));
            }
            doors.push_back(edge_to_output(edge_id(c->path[level], c->path[level + 1]), 2 * level));
            int first_switch = level + 1, last_switch = ec - level - 1;
            for (int bit = 0; bit <= level; ++bit) {
                int index = first_switch + (last_switch - first_switch) * bit / level;
                int v = c->path[index];
                switches.push_back({v / N, v % N, bit});
            }
            int first_post = ec - level - 1;
            for (int x = 0; x < level; ++x) {
                doors.push_back(edge_to_output(
                    edge_id(c->path[first_post + x], c->path[first_post + x + 1]), 2 * x));
            }
            doors.push_back(edge_to_output(
                edge_id(c->path[ec - 1], c->path[ec]), 2 * level + 1));
            seal_set.insert(c->seals.begin(), c->seals.end());
        }
        vector<int> seals(seal_set.begin(), seal_set.end());
        sort(seals.begin(), seals.end());
        for (int e : seals) doors.push_back(edge_to_output(e, 19));
        return {doors, switches};
    }

    int shortest_actions(int used_bits, const vector<DoorOut>& doors,
                         const vector<SwitchOut>& switches) const {
        vector<int> door_map(V * V, -1), switch_map(V, -1);
        for (const auto& d : doors) {
            int a = d.i * N + d.j;
            int b = d.d == 0 ? (d.i + 1) * N + d.j : d.i * N + d.j + 1;
            door_map[edge_id(a, b)] = d.type;
        }
        for (const auto& s : switches) switch_map[s.i * N + s.j] = s.type;
        int masks = 1 << used_bits;
        vector<int> dist(V * masks, -1), q(V * masks);
        int head = 0, tail = 0;
        q[tail++] = start;
        dist[start] = 0;
        while (head < tail) {
            int state = q[head++], mask = state / V, v = state % V;
            if (v == goal) return dist[state];
            if (switch_map[v] >= 0) {
                int ns = (mask ^ (1 << switch_map[v])) * V + v;
                if (dist[ns] < 0) {
                    dist[ns] = dist[state] + 1;
                    q[tail++] = ns;
                }
            }
            for (int w : adj[v]) {
                int type = door_map[edge_id(v, w)];
                if (type >= 0 && ((mask >> (type / 2)) & 1) != type % 2) continue;
                int ns = mask * V + w;
                if (dist[ns] < 0) {
                    dist[ns] = dist[state] + 1;
                    q[tail++] = ns;
                }
            }
        }
        return -1;
    }

    SwitchOut choose_level0(const vector<const Candidate*>& selected,
                            const vector<int>& final_edges,
                            const vector<SwitchOut>& switches) const {
        vector<int> blocked = final_edges;
        for (const Candidate* c : selected) {
            blocked.insert(blocked.end(), c->edges.begin(), c->edges.end());
            blocked.insert(blocked.end(), c->seals.begin(), c->seals.end());
        }
        sort_unique(blocked);
        vector<char> comp = component(blocked);
        vector<char> occupied(V, false);
        for (const auto& s : switches) occupied[s.i * N + s.j] = true;
        vector<int> cells;
        for (int v : vertices) if (comp[v] && !occupied[v] && v != goal) cells.push_back(v);
        if (cells.empty()) cells.push_back(start);
        vector<int> portals{start};
        for (const Candidate* c : selected) {
            portals.push_back(c->path.front());
            portals.push_back(c->path.back());
        }
        vector<vector<int>> portal_dist;
        for (int p : portals) portal_dist.push_back(distances(p, blocked));
        sort(cells.begin(), cells.end(), [&](int a, int b) {
            auto h = [&](int v) {
                int sum = 0, mx = 0;
                for (const auto& d : portal_dist) if (d[v] >= 0) {
                    sum += d[v];
                    mx = max(mx, d[v]);
                }
                return sum + mx * 4;
            };
            return h(a) > h(b);
        });
        int best = cells[0];
        if (debug) cerr << "level0 candidates " << cells.size() << " chosen " << best << '\n';
        return {best / N, best % N, 0};
    }

    int add_extra_common_doors(
        int level,
        const vector<const Candidate*>& selected,
        const vector<int>& final_edges,
        vector<DoorOut>& doors,
        vector<SwitchOut>& switches
    ) const {
        if (selected.empty() || level + 1 >= min(K, 9)) return level;
        struct ExtraOption {
            int controlled_edge;
            vector<int> required_seals;
            int rank;
        };

        vector<char> occupied_switch(V, false);
        for (const auto& s : switches) occupied_switch[s.i * N + s.j] = true;

        vector<char> occupied_edge(V * V, false);
        vector<int> occupied_type(V * V, -1);
        for (const auto& d : doors) {
            int a = d.i * N + d.j;
            int b = d.d == 0 ? (d.i + 1) * N + d.j : d.i * N + d.j + 1;
            occupied_edge[edge_id(a, b)] = true;
            occupied_type[edge_id(a, b)] = d.type;
        }

        // 機構前後の追加扉は、同じ端点へ入る他の本線辺をT19で塞ぐ
        // パッケージとして扱う。これにより追加扉を迂回できない。
        vector<vector<ExtraOption>> options(selected.size());
        for (int c = 0; c < (int)selected.size(); ++c) {
            const auto& path = selected[c]->path;
            int ec = path.size() - 1;
            for (int x = 0; x < ec; ++x) {
                int e = edge_id(path[x], path[x + 1]);
                if (!occupied_edge[e]) {
                    options[c].push_back({e, {}, abs(2 * x + 1 - ec)});
                }
            }
            for (int side : {0, ec}) {
                int v = path[side];
                int path_neighbor = side == 0 ? path[1] : path[ec - 1];
                for (int w : adj[v]) {
                    int e = edge_id(v, w);
                    if (!occupied_edge[e] &&
                        find(path.begin(), path.end(), w) == path.end()) {
                        vector<int> seals;
                        bool valid = true;
                        for (int z : adj[v]) {
                            int other = edge_id(v, z);
                            if (z == path_neighbor || other == e) continue;
                            if (occupied_type[other] >= 0 &&
                                occupied_type[other] != 19) {
                                valid = false;
                                break;
                            }
                            if (occupied_type[other] != 19) seals.push_back(other);
                        }
                        if (valid) {
                            sort_unique(seals);
                            options[c].push_back({e, seals, ec + 4 + (int)seals.size() * 3});
                        }
                    }
                }
            }
            sort(options[c].begin(), options[c].end(), [](const auto& a, const auto& b) {
                return tuple(a.rank, a.required_seals.size(), a.controlled_edge) <
                       tuple(b.rank, b.required_seals.size(), b.controlled_edge);
            });
            options[c].erase(
                unique(options[c].begin(), options[c].end(),
                       [](const auto& a, const auto& b) {
                           return a.controlled_edge == b.controlled_edge;
                       }),
                options[c].end());
        }

        // 新スイッチは、機構を通らず到達できる本線側へ置く。
        vector<int> blocked = final_edges;
        for (const Candidate* c : selected) {
            blocked.insert(blocked.end(), c->edges.begin(), c->edges.end());
            blocked.insert(blocked.end(), c->seals.begin(), c->seals.end());
        }
        sort_unique(blocked);
        vector<char> core = component(blocked);
        vector<int> switch_distance(V, 1 << 20);
        queue<int> switch_queue;
        for (const auto& s : switches) {
            int v = s.i * N + s.j;
            if (!core[v] || switch_distance[v] == 0) continue;
            switch_distance[v] = 0;
            switch_queue.push(v);
        }
        while (!switch_queue.empty()) {
            int v = switch_queue.front();
            switch_queue.pop();
            for (int w : adj[v]) {
                if (!core[w] || switch_distance[w] <= switch_distance[v] + 1) continue;
                switch_distance[w] = switch_distance[v] + 1;
                switch_queue.push(w);
            }
        }
        vector<int> endpoints;
        for (const Candidate* c : selected) {
            endpoints.push_back(c->path.front());
            endpoints.push_back(c->path.back());
        }

        // 全機構端点より玉座側にある未使用橋。追加bitは奇数扉にする。
        vector<int> throne_edges;
        vector<int> gadget_blocked;
        for (const Candidate* c : selected) {
            gadget_blocked.insert(gadget_blocked.end(), c->edges.begin(), c->edges.end());
            gadget_blocked.insert(gadget_blocked.end(), c->seals.begin(), c->seals.end());
        }
        sort_unique(gadget_blocked);
        for (int bridge : sg_bridges(gadget_blocked)) {
            vector<int> cut = gadget_blocked;
            cut.insert(lower_bound(cut.begin(), cut.end(), bridge), bridge);
            vector<char> side = component(cut);
            bool ok = !side[goal];
            for (int v : endpoints) ok &= side[v];
            if (ok && !occupied_edge[bridge]) throne_edges.push_back(bridge);
        }
        reverse(throne_edges.begin(), throne_edges.end());

        vector<int> start_dist = distances(start, blocked);

        int highest_bit = level;
        int edge_slot = 0;
        for (int bit = level + 1; bit < min(K, 9); ++bit) {
            vector<int> active;
            for (int c = 0; c < (int)selected.size(); ++c) {
                if (edge_slot < (int)options[c].size()) active.push_back(c);
            }
            if (active.empty()) break;

            vector<DoorOut> added_doors;
            unordered_set<int> layer_edges;
            unordered_set<int> controlled_edges;
            unordered_set<int> seal_edges;
            for (int c : active) {
                const ExtraOption& option = options[c][edge_slot];
                int e = option.controlled_edge;
                if (occupied_edge[e] || !layer_edges.insert(e).second) continue;
                bool valid = true;
                for (int seal : option.required_seals) {
                    if (controlled_edges.count(seal) ||
                        (occupied_type[seal] >= 0 && occupied_type[seal] != 19)) {
                        valid = false;
                        break;
                    }
                }
                if (!valid) {
                    layer_edges.erase(e);
                    continue;
                }
                int parity = selected[c]->level & 1;
                controlled_edges.insert(e);
                added_doors.push_back(edge_to_output(e, 2 * bit + parity));
                for (int seal : option.required_seals) {
                    if (occupied_type[seal] == 19 || !seal_edges.insert(seal).second) continue;
                    layer_edges.insert(seal);
                    added_doors.push_back(edge_to_output(seal, 19));
                }
            }
            while (!throne_edges.empty()) {
                int e = throne_edges.back();
                throne_edges.pop_back();
                if (occupied_edge[e] || layer_edges.count(e)) continue;
                layer_edges.insert(e);
                controlled_edges.insert(e);
                added_doors.push_back(edge_to_output(e, 2 * bit + 1));
                break;
            }
            if (added_doors.empty()) break;
            if ((int)doors.size() + (int)added_doors.size() > M) break;

            vector<int> cells;
            for (int v : vertices) {
                if (core[v] && !occupied_switch[v] && v != goal) cells.push_back(v);
            }
            sort(cells.begin(), cells.end(),
                 [&](int a, int b) {
                     return pair(switch_distance[a], start_dist[a]) >
                            pair(switch_distance[b], start_dist[b]);
                 });
            if (cells.size() > 24) cells.resize(24);

            vector<DoorOut> trial_doors = doors;
            trial_doors.insert(trial_doors.end(), added_doors.begin(), added_doors.end());
            int chosen = -1;
            for (int v : cells) {
                vector<SwitchOut> trial_switches = switches;
                trial_switches.push_back({v / N, v % N, bit});
                int t = shortest_actions(bit + 1, trial_doors, trial_switches);
                if (t >= 0) {
                    chosen = v;
                    break;
                }
            }
            if (chosen < 0) {
                // この共通bitでは成立しない。辺は消費せず次の種類を試す。
                continue;
            }

            doors.insert(doors.end(), added_doors.begin(), added_doors.end());
            switches.push_back({chosen / N, chosen % N, bit});
            occupied_switch[chosen] = true;
            // 次の追加スイッチは今回の配置からも離す。
            queue<int> update_queue;
            if (switch_distance[chosen] > 0) {
                switch_distance[chosen] = 0;
                update_queue.push(chosen);
            }
            while (!update_queue.empty()) {
                int v = update_queue.front();
                update_queue.pop();
                for (int w : adj[v]) {
                    if (!core[w] || switch_distance[w] <= switch_distance[v] + 1) continue;
                    switch_distance[w] = switch_distance[v] + 1;
                    update_queue.push(w);
                }
            }
            for (int e : layer_edges) occupied_edge[e] = true;
            for (int e : controlled_edges) occupied_type[e] = 2 * bit;
            for (int e : seal_edges) occupied_type[e] = 19;
            ++edge_slot;
            highest_bit = bit;
            if (debug) {
                cerr << "extra common switch " << bit
                     << " doors " << added_doors.size()
                     << " cell " << chosen << '\n';
            }
        }
        return highest_bit;
    }

    void remove_duplicate_doors(vector<DoorOut>& doors) const {
        vector<DoorOut> unique_doors;
        vector<char> used(V * V, false);
        unique_doors.reserve(doors.size());
        for (const auto& d : doors) {
            int a = d.i * N + d.j;
            int b = d.d == 0 ? (d.i + 1) * N + d.j : d.i * N + d.j + 1;
            int e = edge_id(a, b);
            if (used[e]) {
                if (debug) cerr << "removed duplicate door edge " << e << '\n';
                continue;
            }
            used[e] = true;
            unique_doors.push_back(d);
        }
        doors.swap(unique_doors);
    }

    int improve_exactly(
        int level,
        int highest_bit,
        const vector<const Candidate*>& selected,
        vector<DoorOut>& doors,
        vector<SwitchOut>& switches
    ) const {
        int used_bits = highest_bit + 1;
        int best_t = shortest_actions(used_bits, doors, switches);
        if (best_t < 0 || time_up()) return best_t;

        mt19937 rng(0x67A11CEu + (unsigned)best_t);

        // 追加スイッチだけを移動する。基本リセット機構のスイッチは固定。
        bool improved = true;
        while (improved && !time_up()) {
            improved = false;
            for (int si = 0; si < (int)switches.size() && !time_up(); ++si) {
                if (switches[si].type <= level) continue;

                vector<char> occupied(V, false);
                for (int j = 0; j < (int)switches.size(); ++j) {
                    if (j != si) occupied[switches[j].i * N + switches[j].j] = true;
                }
                vector<int> candidates_cells;
                for (int v : vertices) {
                    if (!occupied[v] && v != goal) candidates_cells.push_back(v);
                }
                shuffle(candidates_cells.begin(), candidates_cells.end(), rng);
                if (candidates_cells.size() > 28) candidates_cells.resize(28);

                SwitchOut original = switches[si];
                SwitchOut best_switch = original;
                int local_best = best_t;
                for (int v : candidates_cells) {
                    if (time_up()) break;
                    switches[si] = {v / N, v % N, original.type};
                    int t = shortest_actions(used_bits, doors, switches);
                    if (t > local_best) {
                        local_best = t;
                        best_switch = switches[si];
                    }
                }
                switches[si] = best_switch;
                if (local_best > best_t) {
                    best_t = local_best;
                    improved = true;
                    if (debug) {
                        cerr << "exact move switch " << original.type
                             << " T " << best_t << '\n';
                    }
                }
            }
        }

        // 余った扉を、効果が確認できたものだけ貪欲追加する。
        vector<char> occupied_edge(V * V, false);
        vector<int> occupied_type(V * V, -1);
        for (const auto& d : doors) {
            int a = d.i * N + d.j;
            int b = d.d == 0 ? (d.i + 1) * N + d.j : d.i * N + d.j + 1;
            occupied_edge[edge_id(a, b)] = true;
            occupied_type[edge_id(a, b)] = d.type;
        }

        // 追加スイッチを小部屋化する。
        // 入口1辺を別bitの扉にし、残りの入口をT19で永久閉鎖する。
        bool guarded = true;
        while (guarded && !time_up() && (int)doors.size() < M) {
            guarded = false;
            vector<DoorOut> best_package;
            int best_guard_t = best_t;
            int best_switch_type = -1;
            for (const auto& sw : switches) {
                if (sw.type <= level || time_up()) continue;
                int v = sw.i * N + sw.j;
                for (int entry_neighbor : adj[v]) {
                    int entry = edge_id(v, entry_neighbor);
                    if (occupied_edge[entry]) continue;

                    vector<int> seals;
                    bool valid = true;
                    for (int w : adj[v]) {
                        int e = edge_id(v, w);
                        if (e == entry) continue;
                        if (occupied_type[e] >= 0 && occupied_type[e] != 19) {
                            valid = false;
                            break;
                        }
                        if (occupied_type[e] < 0) seals.push_back(e);
                    }
                    if (!valid || 1 + (int)seals.size() + (int)doors.size() > M) continue;

                    for (int bit = 0; bit < used_bits && !time_up(); ++bit) {
                        if (bit == sw.type) continue;
                        for (int parity = 0; parity < 2 && !time_up(); ++parity) {
                            vector<DoorOut> package;
                            package.push_back(edge_to_output(entry, 2 * bit + parity));
                            for (int e : seals) package.push_back(edge_to_output(e, 19));
                            doors.insert(doors.end(), package.begin(), package.end());
                            int t = shortest_actions(used_bits, doors, switches);
                            doors.resize(doors.size() - package.size());
                            if (t > best_guard_t) {
                                best_guard_t = t;
                                best_package = move(package);
                                best_switch_type = sw.type;
                            }
                        }
                    }
                }
            }
            if (!best_package.empty()) {
                for (const auto& d : best_package) {
                    int a = d.i * N + d.j;
                    int b = d.d == 0 ? (d.i + 1) * N + d.j : d.i * N + d.j + 1;
                    int e = edge_id(a, b);
                    occupied_edge[e] = true;
                    occupied_type[e] = d.type;
                    doors.push_back(d);
                }
                best_t = best_guard_t;
                guarded = true;
                if (debug) {
                    cerr << "guard switch " << best_switch_type
                         << " package " << best_package.size()
                         << " T " << best_t << '\n';
                }
            }
        }

        vector<int> candidate_edges;
        unordered_set<int> bridge_set;
        unordered_set<int> gadget_edge_set;
        // S-G橋、機構辺、その他の順。
        for (int e : sg_bridges({})) {
            bridge_set.insert(e);
            if (!occupied_edge[e]) candidate_edges.push_back(e);
        }
        for (const Candidate* c : selected) {
            for (int e : c->edges) {
                gadget_edge_set.insert(e);
                if (!occupied_edge[e]) candidate_edges.push_back(e);
            }
        }
        for (int e : all_edges) if (!occupied_edge[e]) candidate_edges.push_back(e);
        sort_unique(candidate_edges);
        stable_sort(candidate_edges.begin(), candidate_edges.end(), [&](int a, int b) {
            auto priority = [&](int e) {
                if (bridge_set.count(e)) return 0;
                if (gadget_edge_set.count(e)) return 1;
                return 2;
            };
            return priority(a) < priority(b);
        });

        while ((int)doors.size() < M && !time_up()) {
            int chosen_edge = -1, chosen_type = -1, chosen_t = best_t;
            for (int e : candidate_edges) {
                if (occupied_edge[e]) continue;
                for (int bit = 0; bit < used_bits && !time_up(); ++bit) {
                    for (int parity = 0; parity < 2 && !time_up(); ++parity) {
                        doors.push_back(edge_to_output(e, 2 * bit + parity));
                        int t = shortest_actions(used_bits, doors, switches);
                        doors.pop_back();
                        if (t > chosen_t) {
                            chosen_t = t;
                            chosen_edge = e;
                            chosen_type = 2 * bit + parity;
                        }
                    }
                }
                if (time_up()) break;
            }
            if (chosen_edge < 0) break;
            doors.push_back(edge_to_output(chosen_edge, chosen_type));
            occupied_edge[chosen_edge] = true;
            best_t = chosen_t;
            if (debug) {
                cerr << "exact add door type " << chosen_type
                     << " T " << best_t << '\n';
            }
        }
        return best_t;
    }

public:
    Solver(int n, int m, int k, vector<string> b, bool dbg)
        : N(n), M(m), K(k), V(n * n), board(move(b)), adj(V), debug(dbg), goal(V - 1) {
        for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) if (board[i][j] == '.') {
            int v = i * N + j;
            vertices.push_back(v);
            for (auto [di, dj] : array<pair<int, int>, 4>{{{-1,0},{1,0},{0,-1},{0,1}}}) {
                int ni = i + di, nj = j + dj;
                if (0 <= ni && ni < N && 0 <= nj && nj < N && board[ni][nj] == '.') {
                    adj[v].push_back(ni * N + nj);
                }
            }
        }
        for (int v : vertices) for (int w : adj[v]) if (v < w) all_edges.push_back(edge_id(v, w));
        sort(all_edges.begin(), all_edges.end());
        corridors = list_corridors();
        cycles = list_cycles();
    }

    pair<vector<DoorOut>, vector<SwitchOut>> solve() {
        auto [level, selected, final_edges] = select_layout();
        auto [doors, switches] = build_gadgets(selected);
        for (int bit = 0; bit < (int)final_edges.size(); ++bit) {
            doors.push_back(edge_to_output(final_edges[bit], 2 * bit + 1));
        }
        switches.push_back(choose_level0(selected, final_edges, switches));
        int highest_bit = add_extra_common_doors(
            level, selected, final_edges, doors, switches
        );
        remove_duplicate_doors(doors);
        int improved_t = improve_exactly(
            level, highest_bit, selected, doors, switches
        );
        if (debug) {
            cerr << "selected max level " << level << '\n';
            for (const Candidate* c : selected) {
                cerr << "gadget " << c->level << " length " << c->path.size() - 1
                     << " bypass " << c->bypass_length << " seals " << c->seals.size() << '\n';
            }
            cerr << "doors " << doors.size() << " switches " << switches.size() << '\n';
            cerr << "verified T " << improved_t << '\n';
        }
        return {doors, switches};
    }
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    bool debug = false;
    for (int i = 1; i < argc; ++i) if (string(argv[i]) == "--debug") debug = true;
    int N, M, K;
    if (!(cin >> N >> M >> K)) return 0;
    vector<string> board(N);
    for (auto& row : board) cin >> row;
    Solver solver(N, M, K, board, debug);
    auto [doors, switches] = solver.solve();
    cout << doors.size() << '\n';
    for (const auto& d : doors) cout << d.d << ' ' << d.i << ' ' << d.j << ' ' << d.type << '\n';
    cout << switches.size() << '\n';
    for (const auto& s : switches) cout << s.i << ' ' << s.j << ' ' << s.type << '\n';
}

提出情報

提出日時
問題 A - Castle Renovation with Linked Doors
ユーザ nikuroll
言語 C++23 (GCC 15.2.0)
得点 801218069
コード長 52340 Byte
結果 AC
実行時間 1553 ms
メモリ 21960 KiB

ジャッジ結果

セット名 test_ALL
得点 / 配点 801218069 / 3000000000
結果
AC × 150
セット名 テストケース
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
ケース名 結果 実行時間 メモリ
test_0000.txt AC 1552 ms 9616 KiB
test_0001.txt AC 1551 ms 7092 KiB
test_0002.txt AC 1552 ms 12816 KiB
test_0003.txt AC 1551 ms 8788 KiB
test_0004.txt AC 344 ms 16508 KiB
test_0005.txt AC 1552 ms 15080 KiB
test_0006.txt AC 1552 ms 15752 KiB
test_0007.txt AC 1552 ms 12480 KiB
test_0008.txt AC 1551 ms 12432 KiB
test_0009.txt AC 1551 ms 7116 KiB
test_0010.txt AC 1551 ms 6816 KiB
test_0011.txt AC 1552 ms 19876 KiB
test_0012.txt AC 1551 ms 10460 KiB
test_0013.txt AC 1551 ms 11160 KiB
test_0014.txt AC 1552 ms 12084 KiB
test_0015.txt AC 1552 ms 8432 KiB
test_0016.txt AC 1552 ms 15608 KiB
test_0017.txt AC 1552 ms 13796 KiB
test_0018.txt AC 1552 ms 11916 KiB
test_0019.txt AC 853 ms 10508 KiB
test_0020.txt AC 524 ms 18548 KiB
test_0021.txt AC 1551 ms 9936 KiB
test_0022.txt AC 1553 ms 13260 KiB
test_0023.txt AC 813 ms 15896 KiB
test_0024.txt AC 1551 ms 6448 KiB
test_0025.txt AC 1552 ms 14044 KiB
test_0026.txt AC 1551 ms 7724 KiB
test_0027.txt AC 1551 ms 7888 KiB
test_0028.txt AC 1552 ms 16292 KiB
test_0029.txt AC 1552 ms 17276 KiB
test_0030.txt AC 795 ms 6192 KiB
test_0031.txt AC 709 ms 9756 KiB
test_0032.txt AC 1552 ms 12480 KiB
test_0033.txt AC 1552 ms 17216 KiB
test_0034.txt AC 1552 ms 14692 KiB
test_0035.txt AC 1552 ms 16416 KiB
test_0036.txt AC 713 ms 11516 KiB
test_0037.txt AC 1552 ms 13060 KiB
test_0038.txt AC 1551 ms 8508 KiB
test_0039.txt AC 298 ms 11748 KiB
test_0040.txt AC 1551 ms 8988 KiB
test_0041.txt AC 1438 ms 13660 KiB
test_0042.txt AC 1552 ms 13268 KiB
test_0043.txt AC 1552 ms 10260 KiB
test_0044.txt AC 1551 ms 9872 KiB
test_0045.txt AC 1552 ms 11056 KiB
test_0046.txt AC 1551 ms 10944 KiB
test_0047.txt AC 1552 ms 19172 KiB
test_0048.txt AC 1552 ms 15356 KiB
test_0049.txt AC 1409 ms 15056 KiB
test_0050.txt AC 1552 ms 7816 KiB
test_0051.txt AC 1552 ms 14068 KiB
test_0052.txt AC 1367 ms 12868 KiB
test_0053.txt AC 1552 ms 15344 KiB
test_0054.txt AC 856 ms 8492 KiB
test_0055.txt AC 1552 ms 9152 KiB
test_0056.txt AC 1551 ms 9492 KiB
test_0057.txt AC 1552 ms 13964 KiB
test_0058.txt AC 1551 ms 10532 KiB
test_0059.txt AC 1552 ms 13128 KiB
test_0060.txt AC 921 ms 13564 KiB
test_0061.txt AC 1551 ms 11992 KiB
test_0062.txt AC 1552 ms 13564 KiB
test_0063.txt AC 1551 ms 10168 KiB
test_0064.txt AC 1551 ms 8248 KiB
test_0065.txt AC 1170 ms 15376 KiB
test_0066.txt AC 1551 ms 8852 KiB
test_0067.txt AC 1552 ms 13436 KiB
test_0068.txt AC 615 ms 10924 KiB
test_0069.txt AC 1552 ms 12680 KiB
test_0070.txt AC 1551 ms 12144 KiB
test_0071.txt AC 1061 ms 10224 KiB
test_0072.txt AC 290 ms 18000 KiB
test_0073.txt AC 1552 ms 9772 KiB
test_0074.txt AC 1551 ms 7888 KiB
test_0075.txt AC 586 ms 9384 KiB
test_0076.txt AC 1552 ms 19584 KiB
test_0077.txt AC 1210 ms 12008 KiB
test_0078.txt AC 1551 ms 13076 KiB
test_0079.txt AC 1551 ms 6364 KiB
test_0080.txt AC 1552 ms 9292 KiB
test_0081.txt AC 1053 ms 9996 KiB
test_0082.txt AC 666 ms 7964 KiB
test_0083.txt AC 1551 ms 8832 KiB
test_0084.txt AC 1552 ms 14352 KiB
test_0085.txt AC 1552 ms 14152 KiB
test_0086.txt AC 1552 ms 11404 KiB
test_0087.txt AC 1552 ms 16272 KiB
test_0088.txt AC 1551 ms 12668 KiB
test_0089.txt AC 1551 ms 11632 KiB
test_0090.txt AC 1552 ms 14884 KiB
test_0091.txt AC 1553 ms 16744 KiB
test_0092.txt AC 382 ms 15848 KiB
test_0093.txt AC 1479 ms 12844 KiB
test_0094.txt AC 742 ms 12416 KiB
test_0095.txt AC 1552 ms 14296 KiB
test_0096.txt AC 1552 ms 17756 KiB
test_0097.txt AC 273 ms 13004 KiB
test_0098.txt AC 1552 ms 12412 KiB
test_0099.txt AC 1553 ms 16144 KiB
test_0100.txt AC 1551 ms 7004 KiB
test_0101.txt AC 1325 ms 7516 KiB
test_0102.txt AC 1552 ms 15352 KiB
test_0103.txt AC 1552 ms 13192 KiB
test_0104.txt AC 615 ms 11328 KiB
test_0105.txt AC 1385 ms 13988 KiB
test_0106.txt AC 1552 ms 19932 KiB
test_0107.txt AC 1552 ms 15456 KiB
test_0108.txt AC 1551 ms 9808 KiB
test_0109.txt AC 960 ms 11976 KiB
test_0110.txt AC 1552 ms 16804 KiB
test_0111.txt AC 1552 ms 10184 KiB
test_0112.txt AC 1135 ms 7160 KiB
test_0113.txt AC 1552 ms 17236 KiB
test_0114.txt AC 1552 ms 12712 KiB
test_0115.txt AC 1553 ms 14440 KiB
test_0116.txt AC 1552 ms 15704 KiB
test_0117.txt AC 194 ms 9856 KiB
test_0118.txt AC 1552 ms 16580 KiB
test_0119.txt AC 1551 ms 9108 KiB
test_0120.txt AC 1551 ms 7336 KiB
test_0121.txt AC 329 ms 15448 KiB
test_0122.txt AC 1553 ms 13004 KiB
test_0123.txt AC 1552 ms 16304 KiB
test_0124.txt AC 1552 ms 10644 KiB
test_0125.txt AC 1552 ms 19056 KiB
test_0126.txt AC 327 ms 14736 KiB
test_0127.txt AC 1552 ms 20120 KiB
test_0128.txt AC 1552 ms 9968 KiB
test_0129.txt AC 1552 ms 18528 KiB
test_0130.txt AC 1552 ms 11948 KiB
test_0131.txt AC 686 ms 14052 KiB
test_0132.txt AC 1552 ms 15516 KiB
test_0133.txt AC 773 ms 7180 KiB
test_0134.txt AC 1551 ms 7388 KiB
test_0135.txt AC 254 ms 16196 KiB
test_0136.txt AC 574 ms 18948 KiB
test_0137.txt AC 1551 ms 11008 KiB
test_0138.txt AC 1553 ms 11904 KiB
test_0139.txt AC 1552 ms 16736 KiB
test_0140.txt AC 1552 ms 18316 KiB
test_0141.txt AC 1552 ms 13192 KiB
test_0142.txt AC 691 ms 9260 KiB
test_0143.txt AC 1552 ms 11648 KiB
test_0144.txt AC 1450 ms 11408 KiB
test_0145.txt AC 1552 ms 16584 KiB
test_0146.txt AC 1552 ms 17340 KiB
test_0147.txt AC 1552 ms 14252 KiB
test_0148.txt AC 1552 ms 13252 KiB
test_0149.txt AC 1552 ms 21960 KiB