結果

問題 No.3506 All Distance is Square Number
コンテスト
ユーザー rumblycascade7
提出日時 2026-04-18 11:18:37
言語 C++23
(gcc 15.2.0 + boost 1.89.0)
コンパイル:
g++-15 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
AC  
実行時間 223 ms / 2,000 ms
コード長 19,621 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 3,282 ms
コンパイル使用メモリ 277,776 KB
実行使用メモリ 42,028 KB
最終ジャッジ日時 2026-04-18 11:18:54
合計ジャッジ時間 7,347 ms
ジャッジサーバーID
(参考情報)
judge3_0 / judge1_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 29
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <functional>
#include <iostream>
#include <stdexcept>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace std;

struct Edge {
    int u;
    int v;
    int w;
};

static bool is_square(int x) {
    if (x < 0) return false;
    int r = static_cast<int>(sqrt(static_cast<long double>(x)));
    while ((r + 1) * 1LL * (r + 1) <= x) ++r;
    while (r * 1LL * r > x) --r;
    return r * 1LL * r == x;
}

static vector<uint16_t> normalize(vector<int> vals) {
    sort(vals.begin(), vals.end());
    vals.erase(unique(vals.begin(), vals.end()), vals.end());
    vector<uint16_t> out;
    out.reserve(vals.size());
    for (int x : vals) out.push_back(static_cast<uint16_t>(x));
    return out;
}

static bool contains_sum(const vector<uint16_t>& vals, int x) {
    if (x < 0) return false;
    return binary_search(vals.begin(), vals.end(), static_cast<uint16_t>(x));
}

static vector<int> reversed_path(const vector<int>& path) {
    return vector<int>(path.rbegin(), path.rend());
}

class Solver {
public:
    explicit Solver(int n) : N(n) {
        square.assign(11050, false);
        for (int k = 0; k * k < static_cast<int>(square.size()); ++k) square[k * k] = true;
        pair_paths.assign(N + 1, vector<vector<int>>(N + 1));
    }

    void solve() {
        if (N == 2) {
            solve_small_2();
            return;
        }
        if (N == 3) {
            solve_small_3();
            return;
        }
        if (N == 4) {
            solve_small_4();
            return;
        }
        if (N == 5) {
            solve_small_5();
            return;
        }

        init_base_edges();
        build_base_pair_paths();
        build_base_maps();
        init_stage_14();
        extend_edges_and_paths();
        print_answer();
    }

private:
    using SumList = vector<uint16_t>;
    using PathMap = unordered_map<int, vector<int>>;

    int N;
    vector<bool> square;
    vector<Edge> edges;
    vector<vector<vector<int>>> pair_paths;

    static constexpr int HELPER = 7;
    static constexpr int BASE_TAIL = 14;

    array<PathMap, BASE_TAIL + 1> H0{};
    array<PathMap, BASE_TAIL + 1> T0{};
    array<PathMap, BASE_TAIL + 1> N0{};

    vector<vector<SumList>> H;
    vector<vector<SumList>> T;
    vector<vector<SumList>> Nsum;
    vector<int> attach_a;
    vector<int> attach_b;
    vector<int> attach_edge_h;
    vector<int> attach_edge_t;

    unordered_map<long long, vector<int>> memoH;
    unordered_map<long long, vector<int>> memoT;
    unordered_map<long long, vector<int>> memoN;

    static long long make_key(int i, int t, int s) {
        return (static_cast<long long>(t) * 128LL + i) * 20000LL + s;
    }

    void solve_small_2() {
        edges = {{1, 2, 1}};
        pair_paths[1][2] = {1};
        print_answer();
    }

    void solve_small_3() {
        edges = {{1, 2, 16}, {2, 3, 9}, {1, 3, 25}};
        pair_paths[1][2] = {1};
        pair_paths[1][3] = {3};
        pair_paths[2][3] = {2};
        print_answer();
    }

    void solve_small_4() {
        edges = {
            {1, 3, 9},
            {1, 4, 27},
            {2, 3, 16},
            {2, 4, 36},
            {3, 4, 7},
        };
        pair_paths[1][2] = {1, 3};
        pair_paths[1][3] = {1};
        pair_paths[1][4] = {1, 5};
        pair_paths[2][3] = {3};
        pair_paths[2][4] = {4};
        pair_paths[3][4] = {1, 2};
        print_answer();
    }

    void solve_small_5() {
        edges = {
            {1, 2, 144},
            {1, 3, 45},
            {1, 4, 1},
            {2, 3, 81},
            {3, 5, 36},
            {4, 5, 64},
        };
        pair_paths[1][2] = {1};
        pair_paths[1][3] = {1, 4};
        pair_paths[1][4] = {3};
        pair_paths[1][5] = {2, 5};
        pair_paths[2][3] = {4};
        pair_paths[2][4] = {1, 2, 5, 6};
        pair_paths[2][5] = {1, 2, 5};
        pair_paths[3][4] = {5, 6};
        pair_paths[3][5] = {5};
        pair_paths[4][5] = {6};
        print_answer();
    }

    void init_base_edges() {
        static const Edge base[] = {
            {2, 3, 105},
            {1, 2, 37},
            {2, 4, 93},
            {1, 4, 68},
            {5, 6, 60},
            {4, 5, 132},
            {3, 5, 99},
            {3, 4, 27},
            {2, 6, 64},
            {1, 7, 8},
            {2, 7, 6},
            {1, 8, 2},
            {6, 8, 3},
            {1, 9, 5},
            {3, 9, 10},
            {3, 10, 7},
            {8, 10, 11},
            {7, 11, 12},
            {10, 11, 13},
            {9, 12, 14},
            {11, 12, 15},
            {5, 13, 17},
            {12, 13, 18},
            {6, 14, 19},
            {13, 14, 20},
        };
        edges.assign(base, base + 25);
    }

    vector<vector<pair<int, int>>> build_adj(int vertex_limit, int edge_limit) const {
        vector<vector<pair<int, int>>> adj(vertex_limit + 1);
        for (int id = 1; id <= edge_limit; ++id) {
            const Edge& e = edges[id - 1];
            if (e.u > vertex_limit || e.v > vertex_limit) continue;
            adj[e.u].push_back({e.v, id});
            adj[e.v].push_back({e.u, id});
        }
        return adj;
    }

    void build_base_pair_paths() {
        for (int n = 6; n <= min(N, BASE_TAIL); ++n) {
            int edge_limit = 2 * n - 3;
            auto adj = build_adj(n, edge_limit);
            for (int s = 1; s <= n; ++s) {
                for (int t = s + 1; t <= n; ++t) {
                    if (!pair_paths[s][t].empty()) continue;
                    vector<int> cur;
                    vector<int> answer;
                    vector<int> vis(n + 1, 0);
                    vis[s] = 1;
                    function<bool(int, int)> dfs = [&](int u, int sum) {
                        if (u == t) {
                            if (square[sum]) {
                                answer = cur;
                                return true;
                            }
                            return false;
                        }
                        for (auto [v, eid] : adj[u]) {
                            if (vis[v]) continue;
                            vis[v] = 1;
                            cur.push_back(eid);
                            if (dfs(v, sum + edges[eid - 1].w)) return true;
                            cur.pop_back();
                            vis[v] = 0;
                        }
                        return false;
                    };
                    if (!dfs(s, 0)) throw runtime_error("base pair path not found");
                    pair_paths[s][t] = answer;
                }
            }
        }
    }

    void enumerate_paths(
        int src,
        int dst,
        bool forbid_internal_helper,
        PathMap& out
    ) {
        auto adj = build_adj(BASE_TAIL, 25);
        vector<int> cur;
        vector<int> vis(BASE_TAIL + 1, 0);
        vis[src] = 1;
        function<void(int, int)> dfs = [&](int u, int sum) {
            if (u == dst) {
                out.emplace(sum, cur);
                return;
            }
            for (auto [v, eid] : adj[u]) {
                if (vis[v]) continue;
                if (forbid_internal_helper && v == HELPER && v != dst) continue;
                vis[v] = 1;
                cur.push_back(eid);
                dfs(v, sum + edges[eid - 1].w);
                cur.pop_back();
                vis[v] = 0;
            }
        };
        dfs(src, 0);
    }

    void build_base_maps() {
        for (int i = 1; i <= BASE_TAIL; ++i) {
            enumerate_paths(i, HELPER, false, H0[i]);
            enumerate_paths(i, BASE_TAIL, false, T0[i]);
            enumerate_paths(i, BASE_TAIL, true, N0[i]);
        }
    }

    void init_stage_14() {
        int stage_cap = max(N, BASE_TAIL);
        H.assign(stage_cap + 1, {});
        T.assign(stage_cap + 1, {});
        Nsum.assign(stage_cap + 1, {});
        attach_a.assign(stage_cap + 1, 0);
        attach_b.assign(stage_cap + 1, 0);
        attach_edge_h.assign(stage_cap + 1, 0);
        attach_edge_t.assign(stage_cap + 1, 0);

        H[BASE_TAIL].assign(BASE_TAIL + 1, {});
        T[BASE_TAIL].assign(BASE_TAIL + 1, {});
        Nsum[BASE_TAIL].assign(BASE_TAIL + 1, {});
        for (int i = 1; i <= BASE_TAIL; ++i) {
            vector<int> hs, ts, ns;
            hs.reserve(H0[i].size());
            ts.reserve(T0[i].size());
            ns.reserve(N0[i].size());
            for (const auto& [sum, _] : H0[i]) hs.push_back(sum);
            for (const auto& [sum, _] : T0[i]) ts.push_back(sum);
            for (const auto& [sum, _] : N0[i]) ns.push_back(sum);
            H[BASE_TAIL][i] = normalize(hs);
            T[BASE_TAIL][i] = normalize(ts);
            Nsum[BASE_TAIL][i] = normalize(ns);
        }
    }

    bool has_square_with_offset(const SumList& vals, int delta) const {
        for (uint16_t x : vals) {
            int s = static_cast<int>(x) + delta;
            if (s < static_cast<int>(square.size()) && square[s]) return true;
        }
        return false;
    }

    void extend_edges_and_paths() {
        vector<int> used(201, 0);
        for (const auto& e : edges) used[e.w] = 1;

        for (int t = BASE_TAIL; t < N; ++t) {
            vector<int> unused;
            for (int x = 1; x <= 200; ++x) {
                if (!used[x]) unused.push_back(x);
            }

            int choose_a = -1;
            int choose_b = -1;
            for (int ia = 0; ia < static_cast<int>(unused.size()) && choose_a == -1; ++ia) {
                int a = unused[ia];
                vector<char> ok_with_a(t + 1, 0);
                for (int i = 1; i <= t; ++i) {
                    ok_with_a[i] = has_square_with_offset(H[t][i], a);
                }
                for (int ib = ia + 1; ib < static_cast<int>(unused.size()); ++ib) {
                    int b = unused[ib];
                    bool good = true;
                    for (int i = 1; i <= t; ++i) {
                        if (ok_with_a[i]) continue;
                        if (!has_square_with_offset(T[t][i], b)) {
                            good = false;
                            break;
                        }
                    }
                    if (good) {
                        choose_a = a;
                        choose_b = b;
                        break;
                    }
                }
            }
            if (choose_a == -1) throw runtime_error("failed to extend graph");

            int v = t + 1;
            used[choose_a] = used[choose_b] = 1;
            attach_a[v] = choose_a;
            attach_b[v] = choose_b;
            attach_edge_h[v] = static_cast<int>(edges.size()) + 1;
            edges.push_back({HELPER, v, choose_a});
            attach_edge_t[v] = static_cast<int>(edges.size()) + 1;
            edges.push_back({t, v, choose_b});

            H[v].assign(v + 1, {});
            T[v].assign(v + 1, {});
            Nsum[v].assign(v + 1, {});

            for (int i = 1; i <= t; ++i) {
                if (i == HELPER) {
                    vector<int> ns;
                    ns.reserve(Nsum[t][i].size() + 1);
                    ns.push_back(choose_a);
                    for (uint16_t x : Nsum[t][i]) ns.push_back(static_cast<int>(x) + choose_b);
                    Nsum[v][i] = normalize(ns);
                    H[v][i] = {0};
                    T[v][i] = Nsum[v][i];
                } else {
                    vector<int> hs;
                    hs.reserve(H[t][i].size() + Nsum[t][i].size());
                    for (uint16_t x : H[t][i]) hs.push_back(x);
                    for (uint16_t x : Nsum[t][i]) hs.push_back(static_cast<int>(x) + choose_b + choose_a);
                    H[v][i] = normalize(hs);

                    vector<int> ns;
                    ns.reserve(Nsum[t][i].size());
                    for (uint16_t x : Nsum[t][i]) ns.push_back(static_cast<int>(x) + choose_b);
                    Nsum[v][i] = normalize(ns);

                    vector<int> ts;
                    ts.reserve(Nsum[v][i].size() + H[t][i].size());
                    for (uint16_t x : Nsum[v][i]) ts.push_back(x);
                    for (uint16_t x : H[t][i]) ts.push_back(static_cast<int>(x) + choose_a);
                    T[v][i] = normalize(ts);
                }
            }

            vector<int> hs_new;
            hs_new.reserve(Nsum[t][HELPER].size() + 1);
            hs_new.push_back(choose_a);
            for (uint16_t x : Nsum[t][HELPER]) hs_new.push_back(static_cast<int>(x) + choose_b);
            H[v][v] = normalize(hs_new);
            T[v][v] = {0};
            Nsum[v][v] = {0};

            for (int i = 1; i < v; ++i) {
                int best_sum = 1e9;
                int best_kind = -1;
                int best_prev_sum = -1;

                for (uint16_t x : H[t][i]) {
                    int total = static_cast<int>(x) + choose_a;
                    if (total < static_cast<int>(square.size()) && square[total]) {
                        if (total < best_sum || (total == best_sum && best_kind > 0)) {
                            best_sum = total;
                            best_kind = 0;
                            best_prev_sum = x;
                        }
                    }
                }
                for (uint16_t y : T[t][i]) {
                    int total = static_cast<int>(y) + choose_b;
                    if (total < static_cast<int>(square.size()) && square[total]) {
                        if (total < best_sum) {
                            best_sum = total;
                            best_kind = 1;
                            best_prev_sum = y;
                        }
                    }
                }

                if (best_kind == -1) throw runtime_error("failed to build pair path");
                if (best_kind == 0) {
                    auto path = build_H(i, t, best_prev_sum);
                    path.push_back(attach_edge_h[v]);
                    pair_paths[i][v] = move(path);
                } else {
                    auto path = build_T(i, t, best_prev_sum);
                    path.push_back(attach_edge_t[v]);
                    pair_paths[i][v] = move(path);
                }
            }
        }
    }

    vector<int> build_H(int i, int t, int sum) {
        long long key = make_key(i, t, sum);
        auto it = memoH.find(key);
        if (it != memoH.end()) return it->second;

        vector<int> res;
        if (t == BASE_TAIL) {
            auto base_it = H0[i].find(sum);
            if (base_it == H0[i].end()) throw runtime_error("missing H base path");
            res = base_it->second;
        } else {
            int prev = t - 1;
            int a = attach_a[t];
            int b = attach_b[t];
            int eh = attach_edge_h[t];
            int et = attach_edge_t[t];

            if (i == HELPER) {
                if (sum != 0) throw runtime_error("invalid H helper state");
            } else if (i == t) {
                if (sum == a) {
                    res = {eh};
                } else if (contains_sum(Nsum[prev][HELPER], sum - b)) {
                    res = {et};
                    auto tail = reversed_path(build_N(HELPER, prev, sum - b));
                    res.insert(res.end(), tail.begin(), tail.end());
                } else {
                    throw runtime_error("invalid H new-vertex state");
                }
            } else if (contains_sum(H[prev][i], sum)) {
                res = build_H(i, prev, sum);
            } else if (contains_sum(Nsum[prev][i], sum - b - a)) {
                res = build_N(i, prev, sum - b - a);
                res.push_back(et);
                res.push_back(eh);
            } else {
                throw runtime_error("invalid H recurrence");
            }
        }

        memoH.emplace(key, res);
        return res;
    }

    vector<int> build_N(int i, int t, int sum) {
        long long key = make_key(i, t, sum);
        auto it = memoN.find(key);
        if (it != memoN.end()) return it->second;

        vector<int> res;
        if (t == BASE_TAIL) {
            auto base_it = N0[i].find(sum);
            if (base_it == N0[i].end()) throw runtime_error("missing N base path");
            res = base_it->second;
        } else {
            int prev = t - 1;
            int a = attach_a[t];
            int b = attach_b[t];
            int eh = attach_edge_h[t];
            int et = attach_edge_t[t];

            if (i == t) {
                if (sum != 0) throw runtime_error("invalid N new-vertex state");
            } else if (i == HELPER) {
                if (sum == a) {
                    res = {eh};
                } else if (contains_sum(Nsum[prev][i], sum - b)) {
                    res = build_N(i, prev, sum - b);
                    res.push_back(et);
                } else {
                    throw runtime_error("invalid N helper state");
                }
            } else if (contains_sum(Nsum[prev][i], sum - b)) {
                res = build_N(i, prev, sum - b);
                res.push_back(et);
            } else {
                throw runtime_error("invalid N recurrence");
            }
        }

        memoN.emplace(key, res);
        return res;
    }

    vector<int> build_T(int i, int t, int sum) {
        long long key = make_key(i, t, sum);
        auto it = memoT.find(key);
        if (it != memoT.end()) return it->second;

        vector<int> res;
        if (t == BASE_TAIL) {
            auto base_it = T0[i].find(sum);
            if (base_it == T0[i].end()) throw runtime_error("missing T base path");
            res = base_it->second;
        } else {
            int prev = t - 1;
            int a = attach_a[t];
            int b = attach_b[t];
            int eh = attach_edge_h[t];
            int et = attach_edge_t[t];

            if (i == t) {
                if (sum != 0) throw runtime_error("invalid T new-vertex state");
            } else if (i == HELPER) {
                if (sum == a) {
                    res = {eh};
                } else if (contains_sum(Nsum[prev][i], sum - b)) {
                    res = build_N(i, prev, sum - b);
                    res.push_back(et);
                } else {
                    throw runtime_error("invalid T helper state");
                }
            } else if (contains_sum(H[prev][i], sum - a)) {
                res = build_H(i, prev, sum - a);
                res.push_back(eh);
            } else if (contains_sum(Nsum[prev][i], sum - b)) {
                res = build_N(i, prev, sum - b);
                res.push_back(et);
            } else {
                throw runtime_error("invalid T recurrence");
            }
        }

        memoT.emplace(key, res);
        return res;
    }

    void print_answer() const {
        int edge_count = static_cast<int>(edges.size());
        if (N >= 6 && N < BASE_TAIL) edge_count = 2 * N - 3;

        cout << edge_count << '\n';
        for (int i = 0; i < edge_count; ++i) {
            const auto& e = edges[i];
            cout << e.u << ' ' << e.v << ' ' << e.w << '\n';
        }
        for (int i = 1; i <= N; ++i) {
            for (int j = i + 1; j <= N; ++j) {
                const auto& p = pair_paths[i][j];
                cout << p.size();
                for (int eid : p) cout << ' ' << eid;
                cout << '\n';
            }
        }
    }
};

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

    int N;
    cin >> N;
    Solver solver(N);
    solver.solve();
    return 0;
}
0