Submission #74068675


Source Code Expand

#include <bits/stdc++.h>

using namespace std;

namespace utility {
struct Timer {
  chrono::steady_clock::time_point start;

  void begin() { start = chrono::steady_clock::now(); }

  double elapsed_ms() const {
    using namespace std::chrono;
    return (double)duration_cast<milliseconds>(steady_clock::now() - start)
        .count();
  }
} timer;
} // namespace utility

inline unsigned int rand_int() {
  static unsigned int tx = 123456789;
  static unsigned int ty = 362436069;
  static unsigned int tz = 521288629;
  static unsigned int tw = 88675123;
  unsigned int tt = tx ^ (tx << 11);
  tx = ty;
  ty = tz;
  tz = tw;
  return tw = (tw ^ (tw >> 19)) ^ (tt ^ (tt >> 8));
}

inline double rand_double() {
  return (double)(rand_int() % 1000000000U) / 1000000000.0;
}

struct Node {
  int vid;
  int priority;
  int sz;
  bool rev;
  long long weight;
  long long sum_weight;
  long long sum_index_weight;
  Node *l;
  Node *r;
  Node *p;

  Node(int vid_, long long weight_)
      : vid(vid_), priority((int)rand_int()), sz(1), rev(false),
        weight(weight_), sum_weight(weight_), sum_index_weight(0), l(nullptr),
        r(nullptr), p(nullptr) {}
};

inline int node_size(Node *t) { return t == nullptr ? 0 : t->sz; }

inline long long node_sum_weight(Node *t) {
  return t == nullptr ? 0LL : t->sum_weight;
}

inline long long node_sum_index_weight(Node *t) {
  return t == nullptr ? 0LL : t->sum_index_weight;
}

void toggle_reverse(Node *t) {
  if (t == nullptr)
    return;
  t->rev = !t->rev;
  swap(t->l, t->r);
  t->sum_index_weight = 1LL * (t->sz - 1) * t->sum_weight - t->sum_index_weight;
}

void push(Node *t) {
  if (t == nullptr || !t->rev)
    return;
  toggle_reverse(t->l);
  toggle_reverse(t->r);
  t->rev = false;
}

void pull(Node *t) {
  if (t == nullptr)
    return;
  if (t->l != nullptr)
    t->l->p = t;
  if (t->r != nullptr)
    t->r->p = t;
  const int left_size = node_size(t->l);
  const long long right_sum_weight = node_sum_weight(t->r);
  t->sz = 1 + node_size(t->l) + node_size(t->r);
  t->sum_weight = node_sum_weight(t->l) + t->weight + right_sum_weight;
  t->sum_index_weight =
      node_sum_index_weight(t->l) + 1LL * left_size * t->weight +
      node_sum_index_weight(t->r) + 1LL * (left_size + 1) * right_sum_weight;
}

Node *merge(Node *a, Node *b) {
  if (a == nullptr) {
    if (b != nullptr)
      b->p = nullptr;
    return b;
  }
  if (b == nullptr) {
    if (a != nullptr)
      a->p = nullptr;
    return a;
  }
  if (a->priority > b->priority) {
    push(a);
    a->r = merge(a->r, b);
    if (a->r != nullptr)
      a->r->p = a;
    pull(a);
    a->p = nullptr;
    return a;
  }
  push(b);
  b->l = merge(a, b->l);
  if (b->l != nullptr)
    b->l->p = b;
  pull(b);
  b->p = nullptr;
  return b;
}

void split(Node *t, int left_count, Node *&a, Node *&b) {
  if (t == nullptr) {
    a = nullptr;
    b = nullptr;
    return;
  }
  push(t);
  if (node_size(t->l) >= left_count) {
    split(t->l, left_count, a, t->l);
    if (t->l != nullptr)
      t->l->p = t;
    b = t;
    b->p = nullptr;
    pull(b);
    return;
  }
  split(t->r, left_count - node_size(t->l) - 1, t->r, b);
  if (t->r != nullptr)
    t->r->p = t;
  a = t;
  a->p = nullptr;
  pull(a);
}

int index_of(Node *x) {
  Node *stack_buf[128];
  int sz = 0;
  Node *cur = x;
  while (cur != nullptr && sz < 128) {
    stack_buf[sz++] = cur;
    cur = cur->p;
  }
  vector<Node *> spill;
  spill.reserve(16);
  while (cur != nullptr) {
    spill.push_back(cur);
    cur = cur->p;
  }
  for (int i = (int)spill.size() - 1; i >= 0; --i)
    push(spill[i]);
  for (int i = sz - 1; i >= 0; --i)
    push(stack_buf[i]);

  int idx = node_size(x->l);
  cur = x;
  while (cur->p != nullptr) {
    if (cur == cur->p->r)
      idx += node_size(cur->p->l) + 1;
    cur = cur->p;
  }
  return idx;
}

void collect_order(Node *t, vector<int> &order) {
  if (t == nullptr)
    return;
  push(t);
  collect_order(t->l, order);
  order.push_back(t->vid);
  collect_order(t->r, order);
}

struct Move {
  int old1;
  int old2;
  int new1;
  int new2;
};

struct Solver {
  struct MoveKeyHash {
    size_t operator()(const array<int, 4> &k) const {
      size_t h = 1469598103934665603ULL;
      h ^= (size_t)k[0] + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
      h ^= (size_t)k[1] + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
      h ^= (size_t)k[2] + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
      h ^= (size_t)k[3] + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
      return h;
    }
  };

  static constexpr double TIME_LIMIT_MS = 2950.0;
  static constexpr double START_TEMP = 2.0e8;
  static constexpr double END_TEMP = 1.0e7;
  static constexpr double OR_START_TEMP = 2.0e8;
  static constexpr double OR_END_TEMP = 1.0e7;

  int N = 0;
  int V = 0;
  vector<int> A;
  vector<int> row_id;
  vector<int> col_id;

  vector<vector<int>> neighbors;
  vector<array<int, 9>> edge_id_dir;
  vector<pair<int, int>> edges;

  vector<Move> moves;
  vector<vector<int>> incident_moves;
  vector<char> edge_used;
  vector<int> active_moves;
  vector<int> active_pos;

  vector<Node *> node_of;
  Node *root = nullptr;
  vector<int> vtx_pos;   // vtx_pos[v] = position of vertex v in the path
  vector<int> cur_order; // cur_order[i] = vertex at position i
  vector<long long> prefix_A; // prefix_A[i] = sum(A[cur_order[0..i-1]])

  long long current_objective = 0;
  long long best_objective = LLONG_MIN;
  long long iterations = 0;
  vector<int> best_order;

  static constexpr int DR[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
  static constexpr int DC[8] = {-1, 0, 1, -1, 1, -1, 0, 1};

  void input() {
    cin >> N;
    V = N * N;
    A.assign(V, 0);
    row_id.assign(V, 0);
    col_id.assign(V, 0);
    for (int r = 0; r < N; ++r) {
      for (int c = 0; c < N; ++c) {
        const int id = vertex_id(r, c);
        cin >> A[id];
        row_id[id] = r;
        col_id[id] = c;
      }
    }
  }

  int vertex_id(int r, int c) const { return r * N + c; }

  pair<int, int> rc(int v) const { return {v / N, v % N}; }

  bool in_board(int r, int c) const {
    return 0 <= r && r < N && 0 <= c && c < N;
  }

  static int dir_code(int dr, int dc) { return (dr + 1) * 3 + (dc + 1); }

  bool is_adjacent(int u, int v) const {
    if (u == v)
      return false;
    return abs(row_id[u] - row_id[v]) <= 1 && abs(col_id[u] - col_id[v]) <= 1;
  }

  int find_edge_id(int u, int v) const {
    if (!is_adjacent(u, v))
      return -1;
    return edge_id_dir[u]
                      [dir_code(row_id[v] - row_id[u], col_id[v] - col_id[u])];
  }

  long long calc_objective(const vector<int> &order) const {
    long long objective = 0;
    for (int i = 0; i < V; ++i)
      objective += 1LL * i * A[order[i]];
    return objective;
  }

  bool is_valid_hamilton_path(const vector<int> &order) const {
    if ((int)order.size() != V)
      return false;
    vector<char> used(V, false);
    for (int i = 0; i < V; ++i) {
      const int v = order[i];
      if (v < 0 || v >= V || used[v])
        return false;
      used[v] = true;
      if (i > 0 && !is_adjacent(order[i - 1], v))
        return false;
    }
    return true;
  }

  vector<int> build_plain_snake_order() const {
    vector<int> order;
    order.reserve(V);
    for (int r = 0; r < N; ++r) {
      if ((r & 1) == 0) {
        for (int c = 0; c < N; ++c)
          order.push_back(vertex_id(r, c));
      } else {
        for (int c = N - 1; c >= 0; --c)
          order.push_back(vertex_id(r, c));
      }
    }
    return order;
  }

  vector<int> build_two_column_round_trip_order(bool start_from_bottom) const {
    if ((N & 1) != 0 || N < 4)
      return build_plain_snake_order();

    vector<int> order;
    order.reserve(V);
    vector<char> used(V, false);
    bool ok = true;
    const int strips = N / 2;

    auto add_cell = [&](int r, int c) {
      if (!ok)
        return;
      if (r < 0 || r >= N || c < 0 || c >= N) {
        ok = false;
        return;
      }
      const int v = vertex_id(r, c);
      if (used[v]) {
        ok = false;
        return;
      }
      if (!order.empty() && !is_adjacent(order.back(), v)) {
        ok = false;
        return;
      }
      used[v] = true;
      order.push_back(v);
    };

    auto choose_outbound_col = [&](int cL, int cR, int r, int forced_col) {
      if (forced_col != -1)
        return forced_col;
      return (A[vertex_id(r, cL)] <= A[vertex_id(r, cR)]) ? cL : cR;
    };

    function<void(int)> from_top;
    function<void(int)> from_bottom;

    from_top = [&](int s) {
      const int cL = 2 * s;
      const int cR = cL + 1;
      const bool is_last = (s + 1 == strips);

      add_cell(0, cL);
      add_cell(0, cR);

      vector<int> inbound_col(N, -1);
      for (int r = 1; r < N; ++r) {
        int forced = -1;
        if (r == 1)
          forced = cR;
        if (!is_last && r == N - 2)
          forced = cL;
        if (r == N - 1)
          forced = cL;
        const int out_col = choose_outbound_col(cL, cR, r, forced);
        inbound_col[r] = (out_col == cL) ? cR : cL;
        add_cell(r, out_col);
      }

      add_cell(N - 1, cR);
      if (!is_last) {
        from_bottom(s + 1);
        add_cell(N - 2, cR);
        for (int r = N - 3; r >= 2; --r)
          add_cell(r, inbound_col[r]);
      } else {
        for (int r = N - 2; r >= 2; --r)
          add_cell(r, inbound_col[r]);
      }
      add_cell(1, cL);
    };

    from_bottom = [&](int s) {
      const int cL = 2 * s;
      const int cR = cL + 1;
      const bool is_last = (s + 1 == strips);

      add_cell(N - 1, cL);
      add_cell(N - 1, cR);

      vector<int> inbound_col(N, -1);
      for (int r = N - 2; r >= 0; --r) {
        int forced = -1;
        if (r == N - 2)
          forced = cR;
        if (!is_last && r == 1)
          forced = cL;
        if (r == 0)
          forced = cL;
        const int out_col = choose_outbound_col(cL, cR, r, forced);
        inbound_col[r] = (out_col == cL) ? cR : cL;
        add_cell(r, out_col);
      }

      add_cell(0, cR);
      if (!is_last) {
        from_top(s + 1);
        add_cell(1, cR);
        for (int r = 2; r <= N - 3; ++r)
          add_cell(r, inbound_col[r]);
      } else {
        for (int r = 1; r <= N - 3; ++r)
          add_cell(r, inbound_col[r]);
      }
      add_cell(N - 2, cL);
    };

    if (start_from_bottom) {
      from_bottom(0);
    } else {
      from_top(0);
    }

    if (!ok || (int)order.size() != V)
      return build_plain_snake_order();
    return order;
  }

  pair<int, int> transform_rc(int r, int c, int rot, bool mirror) const {
    if (mirror)
      c = N - 1 - c;
    for (int k = 0; k < rot; ++k) {
      const int nr = c;
      const int nc = N - 1 - r;
      r = nr;
      c = nc;
    }
    return {r, c};
  }

  vector<int> transform_order(const vector<int> &base, int rot, bool mirror,
                              bool reverse_path) const {
    vector<int> transformed;
    transformed.reserve(V);
    if (!reverse_path) {
      for (int v : base) {
        const auto [r, c] = rc(v);
        const auto [nr, nc] = transform_rc(r, c, rot, mirror);
        transformed.push_back(vertex_id(nr, nc));
      }
    } else {
      for (int i = V - 1; i >= 0; --i) {
        const auto [r, c] = rc(base[i]);
        const auto [nr, nc] = transform_rc(r, c, rot, mirror);
        transformed.push_back(vertex_id(nr, nc));
      }
    }
    return transformed;
  }

  vector<int> build_initial_order() const {
    vector<int> best = build_plain_snake_order();
    long long best_obj = calc_objective(best);

    auto consider = [&](const vector<int> &cand) {
      if (!is_valid_hamilton_path(cand))
        return;
      const long long obj = calc_objective(cand);
      if (obj > best_obj) {
        best_obj = obj;
        best = cand;
      }
    };

    vector<int> b0 = build_two_column_round_trip_order(true);
    vector<int> b1 = build_two_column_round_trip_order(false);
    for (const vector<int> *base_ptr : {&b0, &b1}) {
      const auto &base = *base_ptr;
      for (int rot = 0; rot < 4; ++rot) {
        for (int mirror = 0; mirror < 2; ++mirror) {
          for (int rev = 0; rev < 2; ++rev) {
            consider(transform_order(base, rot, mirror != 0, rev != 0));
          }
        }
      }
    }
    return best;
  }

  void build_board_graph() {
    neighbors.assign(V, {});
    edge_id_dir.assign(V, {});
    for (int v = 0; v < V; ++v)
      edge_id_dir[v].fill(-1);

    for (int r = 0; r < N; ++r) {
      for (int c = 0; c < N; ++c) {
        const int u = vertex_id(r, c);
        for (int dir = 0; dir < 8; ++dir) {
          const int nr = r + DR[dir];
          const int nc = c + DC[dir];
          if (!in_board(nr, nc))
            continue;
          neighbors[u].push_back(vertex_id(nr, nc));
        }
      }
    }

    for (int u = 0; u < V; ++u) {
      for (int v : neighbors[u]) {
        if (u > v)
          continue;
        const int eid = (int)edges.size();
        edges.push_back({u, v});
        edge_id_dir[u][dir_code(row_id[v] - row_id[u], col_id[v] - col_id[u])] =
            eid;
        edge_id_dir[v][dir_code(row_id[u] - row_id[v], col_id[u] - col_id[v])] =
            eid;
      }
    }
  }

  void add_move_instance(int old1, int old2, int new1, int new2,
                         unordered_set<array<int, 4>, MoveKeyHash> &seen) {
    if (old1 < 0 || old2 < 0 || new1 < 0 || new2 < 0)
      return;
    if (old1 == old2 || new1 == new2)
      return;

    if (old1 > old2)
      swap(old1, old2);
    if (new1 > new2)
      swap(new1, new2);

    const array<int, 4> key = {old1, old2, new1, new2};
    if (!seen.insert(key).second)
      return;

    const int mid = (int)moves.size();
    moves.push_back({old1, old2, new1, new2});
    incident_moves[old1].push_back(mid);
    incident_moves[old2].push_back(mid);
  }

  void build_moves() {
    moves.clear();
    incident_moves.assign(edges.size(), {});
    moves.reserve((N - 1) * max(0, N / 2 - 1) * 4);
    unordered_set<array<int, 4>, MoveKeyHash> seen;
    seen.reserve((size_t)(N - 1) * max(0, N / 2 - 1) * 5);

    // Only use 2x2 neighborhoods across strip boundaries: (1,2), (3,4), ...
    for (int c = 1; c + 1 < N; c += 2) {
      for (int r = 0; r + 1 < N; ++r) {
        const int v00 = vertex_id(r, c);
        const int v10 = vertex_id(r + 1, c);
        const int v01 = vertex_id(r, c + 1);
        const int v11 = vertex_id(r + 1, c + 1);

        const int e_v1 = find_edge_id(v00, v10);
        const int e_v2 = find_edge_id(v01, v11);
        const int e_h1 = find_edge_id(v00, v01);
        const int e_h2 = find_edge_id(v10, v11);
        const int e_d1 = find_edge_id(v00, v11);
        const int e_d2 = find_edge_id(v01, v10);

        // Vertical-parallel <-> diagonal cross.
        add_move_instance(e_v1, e_v2, e_d1, e_d2, seen);
        add_move_instance(e_d1, e_d2, e_v1, e_v2, seen);

        // Horizontal-parallel <-> diagonal cross.
        add_move_instance(e_h1, e_h2, e_d1, e_d2, seen);
        add_move_instance(e_d1, e_d2, e_h1, e_h2, seen);
      }
    }
  }

  void activate_move(int mid) {
    if (active_pos[mid] != -1)
      return;
    active_pos[mid] = (int)active_moves.size();
    active_moves.push_back(mid);
  }

  void deactivate_move(int mid) {
    const int pos = active_pos[mid];
    if (pos == -1)
      return;
    const int back = active_moves.back();
    active_moves[pos] = back;
    active_pos[back] = pos;
    active_moves.pop_back();
    active_pos[mid] = -1;
  }

  void refresh_move(int mid) {
    const Move &move = moves[mid];
    if (edge_used[move.old1] && edge_used[move.old2]) {
      activate_move(mid);
    } else {
      deactivate_move(mid);
    }
  }

  void refresh_incident(int eid) {
    for (int mid : incident_moves[eid])
      refresh_move(mid);
  }

  void build_initial_state(const vector<int> &order) {
    node_of.assign(V, nullptr);
    root = nullptr;
    current_objective = 0;
    vtx_pos.assign(V, 0);
    cur_order = order;
    prefix_A.assign(V + 1, 0);

    for (int p = 0; p < V; ++p) {
      const int v = order[p];
      current_objective += 1LL * p * A[v];
      vtx_pos[v] = p;
      prefix_A[p + 1] = prefix_A[p] + A[v];
      Node *node = new Node(v, A[v]);
      node_of[v] = node;
      root = merge(root, node);
    }

    edge_used.assign(edges.size(), false);
    for (int i = 0; i + 1 < V; ++i) {
      const int eid = find_edge_id(order[i], order[i + 1]);
      edge_used[eid] = true;
    }

    active_pos.assign(moves.size(), -1);
    active_moves.clear();
    for (int mid = 0; mid < (int)moves.size(); ++mid)
      refresh_move(mid);

    best_objective = current_objective;
    best_order = order;
  }

  double temperature() const {
    const double progress =
        min(1.0, utility::timer.elapsed_ms() / TIME_LIMIT_MS);
    return exp(log(START_TEMP) * (1.0 - progress) + log(END_TEMP) * progress);
  }

  double or_temperature() const {
    const double progress =
        min(1.0, utility::timer.elapsed_ms() / TIME_LIMIT_MS);
    return exp(log(OR_START_TEMP) * (1.0 - progress) + log(OR_END_TEMP) * progress);
  }

  bool accept(long long delta, double temp) const {
    if (delta >= 0)
      return true;
    return exp((double)delta / temp) > rand_double();
  }

  void collect_segment_order(Node *t, vector<int> &out) {
    if (t == nullptr)
      return;
    push(t);
    collect_segment_order(t->l, out);
    out.push_back(t->vid);
    collect_segment_order(t->r, out);
  }

  void swap_adjacent_treap(int k) {
    Node *left, *nk, *nk1, *right, *rest, *rest2;
    split(root, k, left, rest);
    split(rest, 1, nk, rest2);
    split(rest2, 1, nk1, right);
    root = merge(merge(merge(left, nk1), nk), right);
  }

  void update_edge(int old_eid, int new_eid) {
    if (old_eid == new_eid)
      return;
    if (old_eid >= 0) {
      edge_used[old_eid] = false;
      refresh_incident(old_eid);
    }
    if (new_eid >= 0) {
      edge_used[new_eid] = true;
      refresh_incident(new_eid);
    }
  }

  void greedy_strip_swaps(int lo, int hi) {
    for (int k = lo; k < hi; ++k) {
      const int u = cur_order[k];
      const int v = cur_order[k + 1];
      // Only horizontal pairs (same row, adjacent columns)
      if (row_id[u] != row_id[v])
        continue;
      if (abs(col_id[u] - col_id[v]) != 1)
        continue;
      // Swap is beneficial if A[u] > A[v] (put larger A at later position k+1)
      if (A[u] <= A[v])
        continue;
      // Check path connectivity after swap
      if (k > 0 && !is_adjacent(cur_order[k - 1], v))
        continue;
      if (k + 2 < V && !is_adjacent(u, cur_order[k + 2]))
        continue;

      // Compute edge changes
      const int old_e_left = (k > 0) ? find_edge_id(cur_order[k - 1], u) : -1;
      const int new_e_left = (k > 0) ? find_edge_id(cur_order[k - 1], v) : -1;
      const int old_e_right =
          (k + 2 < V) ? find_edge_id(v, cur_order[k + 2]) : -1;
      const int new_e_right =
          (k + 2 < V) ? find_edge_id(u, cur_order[k + 2]) : -1;

      // Apply swap
      const long long swap_delta = A[u] - A[v];
      cur_order[k] = v;
      cur_order[k + 1] = u;
      vtx_pos[v] = k;
      vtx_pos[u] = k + 1;
      current_objective += swap_delta;
      prefix_A[k + 1] = prefix_A[k] + A[v];

      // Update Treap
      swap_adjacent_treap(k);

      // Update edge_used
      update_edge(old_e_left, new_e_left);
      update_edge(old_e_right, new_e_right);
    }
  }

  bool apply_move(int mid, double temp) {
    const Move &move = moves[mid];
    const auto [u1, v1] = edges[move.old1];
    const auto [u2, v2] = edges[move.old2];

    int pu1 = vtx_pos[u1];
    int pv1 = vtx_pos[v1];
    int pu2 = vtx_pos[u2];
    int pv2 = vtx_pos[v2];

    if (abs(pu1 - pv1) != 1 || abs(pu2 - pv2) != 1)
      return false;

    int i = pu1 < pv1 ? pu1 : pv1;
    int j = pu2 < pv2 ? pu2 : pv2;
    int a = pu1 < pv1 ? u1 : v1;
    int b = pu1 < pv1 ? v1 : u1;
    int c = pu2 < pv2 ? u2 : v2;
    int d = pu2 < pv2 ? v2 : u2;

    if (i > j) {
      swap(i, j);
      swap(a, c);
      swap(b, d);
    }

    if (j <= i + 1)
      return false;
    if (!is_adjacent(a, c) || !is_adjacent(b, d))
      return false;

    const int new_e1 = find_edge_id(a, c);
    const int new_e2 = find_edge_id(b, d);
    if (new_e1 < 0 || new_e2 < 0)
      return false;

    int actual1 = new_e1;
    int actual2 = new_e2;
    if (actual1 > actual2)
      swap(actual1, actual2);
    int target1 = move.new1;
    int target2 = move.new2;
    if (target1 > target2)
      swap(target1, target2);
    if (actual1 != target1 || actual2 != target2)
      return false;

    Node *left = nullptr;
    Node *mid_seg = nullptr;
    Node *right = nullptr;
    Node *rest = nullptr;
    split(root, i + 1, left, rest);
    split(rest, j - i, mid_seg, right);

    const long long delta =
        1LL * (node_size(mid_seg) - 1) * node_sum_weight(mid_seg) -
        2LL * node_sum_index_weight(mid_seg);
    if (!accept(delta, temp)) {
      root = merge(left, mid_seg);
      root = merge(root, right);
      return false;
    }

    toggle_reverse(mid_seg);
    // Collect and update cur_order/vtx_pos for the reversed segment
    {
      vector<int> seg;
      seg.reserve(j - i);
      collect_segment_order(mid_seg, seg);
      for (int t = 0; t < (int)seg.size(); ++t) {
        cur_order[i + 1 + t] = seg[t];
        vtx_pos[seg[t]] = i + 1 + t;
        prefix_A[i + 1 + t + 1] = prefix_A[i + 1 + t] + A[seg[t]];
      }
    }
    root = merge(left, mid_seg);
    root = merge(root, right);
    current_objective += delta;

    const int changed[4] = {move.old1, move.old2, new_e1, new_e2};
    for (int ii = 0; ii < 4; ++ii) {
      const int eid = changed[ii];
      bool duplicated = false;
      for (int jj = 0; jj < ii; ++jj) {
        if (changed[jj] == eid) {
          duplicated = true;
          break;
        }
      }
      if (duplicated)
        continue;
      const bool should_use = (eid == new_e1 || eid == new_e2);
      if (edge_used[eid] == should_use)
        continue;
      edge_used[eid] = should_use;
      refresh_incident(eid);
    }

    // Greedy within-strip horizontal swaps in the reversed segment
    greedy_strip_swaps(max(0, i), min(V - 1, j));

    if (current_objective > best_objective) {
      best_objective = current_objective;
      best_order = cur_order;
    }
    return true;
  }

  // Or-opt: remove a single vertex and reinsert at a better position
  // Tries ALL grid neighbors of the picked vertex and picks the best insertion.
  bool try_or_opt(double temp) {
    const int k = 1 + rand_int() % (V - 2);
    const int v = cur_order[k];
    const int prev = cur_order[k - 1];
    const int next = cur_order[k + 1];
    if (!is_adjacent(prev, next))
      return false;

    long long best_delta = LLONG_MIN;
    int best_insert = -1;

    for (int nb : neighbors[v]) {
      const int j = vtx_pos[nb];
      if (j == k - 1 || j == k || j == k + 1) continue;

      // Try insert after j: v between path[j] and path[j+1]
      if (j + 1 < V && is_adjacent(v, cur_order[j + 1])) {
        long long delta;
        if (j > k) {
          delta = (long long)(j - k) * A[v] - (prefix_A[j + 1] - prefix_A[k + 1]);
        } else {
          delta = (long long)(j + 1 - k) * A[v] + (prefix_A[k] - prefix_A[j + 1]);
        }
        if (delta > best_delta) { best_delta = delta; best_insert = j; }
      }
      // Try insert before j: v between path[j-1] and path[j]
      if (j - 1 >= 0 && is_adjacent(v, cur_order[j - 1])) {
        int ip = j - 1;
        if (ip == k) continue;
        long long delta;
        if (ip > k) {
          delta = (long long)(ip - k) * A[v] - (prefix_A[ip + 1] - prefix_A[k + 1]);
        } else {
          delta = (long long)(ip + 1 - k) * A[v] + (prefix_A[k] - prefix_A[ip + 1]);
        }
        if (delta > best_delta) { best_delta = delta; best_insert = ip; }
      }
    }

    if (best_insert < 0) return false;
    if (!accept(best_delta, temp)) return false;

    const int insert_pos = best_insert;
    const long long delta = best_delta;

    // --- Accept the move ---
    // Collect old edges to remove and new edges to add
    const int old_e_prev = find_edge_id(prev, v);     // edge (k-1, k)
    const int old_e_next = find_edge_id(v, next);      // edge (k, k+1)
    const int new_e_bridge = find_edge_id(prev, next);  // bridge after removal

    // Update Treap: remove node at position k, insert at new position
    Node *left_part, *mid_node, *right_part, *rest;
    split(root, k, left_part, rest);
    split(rest, 1, mid_node, right_part);
    root = merge(left_part, right_part);

    // Now insert mid_node at position new_pos_of_v
    // After removal, indices >= k shift down by 1.
    int treap_insert_pos;
    if (insert_pos > k) {
      // In the post-removal array, the target index is insert_pos (which shifted to insert_pos - 1 + 1 = insert_pos)
      // Actually: new_pos_of_v = insert_pos; after removal from k < insert_pos,
      // the element at old index insert_pos is now at insert_pos - 1.
      // We want to insert after that element, so at position insert_pos - 1 + 1 = insert_pos... 
      // Wait: after removal, length is V-1. Positions 0..k-1 unchanged, k..V-2 = old k+1..V-1.
      // Old insert_pos > k, so in new array it's at insert_pos - 1.
      // We want to insert AFTER the element at old insert_pos, which is now at insert_pos - 1.
      // Insert at position insert_pos (right after insert_pos - 1).
      treap_insert_pos = insert_pos;
    } else {
      // insert_pos < k. Positions 0..insert_pos unchanged in new array.
      // We want to insert AFTER the element at old insert_pos.
      // In new array, that element is still at insert_pos.
      // Insert at position insert_pos + 1.
      treap_insert_pos = insert_pos + 1;
    }
    
    Node *ins_left, *ins_right;
    split(root, treap_insert_pos, ins_left, ins_right);
    root = merge(merge(ins_left, mid_node), ins_right);

    // Update cur_order and vtx_pos
    if (insert_pos > k) {
      // Shift elements k+1..insert_pos left by 1
      for (int t = k; t < insert_pos; ++t) {
        cur_order[t] = cur_order[t + 1];
        vtx_pos[cur_order[t]] = t;
      }
      cur_order[insert_pos] = v;
      vtx_pos[v] = insert_pos;
    } else {
      // Shift elements insert_pos+1..k-1 right by 1
      for (int t = k; t > insert_pos + 1; --t) {
        cur_order[t] = cur_order[t - 1];
        vtx_pos[cur_order[t]] = t;
      }
      cur_order[insert_pos + 1] = v;
      vtx_pos[v] = insert_pos + 1;
    }

    current_objective += delta;

    // Update edge_used:
    // Old edges removed: (prev, v) and (v, next)
    // New bridge: (prev, next) 
    // Old edge at insertion point removed, new edges at insertion added
    int ins_left_v, ins_right_v;
    if (insert_pos > k) {
      // v is now at position insert_pos
      ins_left_v = cur_order[insert_pos - 1]; // should be == nb or neighbor
      ins_right_v = (insert_pos + 1 < V) ? cur_order[insert_pos + 1] : -1;
    } else {
      // v is now at position insert_pos + 1
      int vp = insert_pos + 1;
      ins_left_v = cur_order[vp - 1];
      ins_right_v = (vp + 1 < V) ? cur_order[vp + 1] : -1;
    }

    const int new_e_left = find_edge_id(ins_left_v, v);
    const int new_e_right = (ins_right_v >= 0) ? find_edge_id(v, ins_right_v) : -1;
    // Old edge at insertion point: (ins_left_v, ins_right_v) if they were adjacent
    const int old_e_insert = (ins_right_v >= 0) ? find_edge_id(ins_left_v, ins_right_v) : -1;

    // Collect all changed edges (deduplicate)
    int changed[6] = {old_e_prev, old_e_next, old_e_insert, new_e_bridge, new_e_left, new_e_right};
    bool should_use[6] = {false, false, false, true, true, true};
    // new_e_right is -1 if v is at end
    if (new_e_right < 0) should_use[5] = false;
    if (old_e_insert < 0) should_use[2] = false;

    for (int ii = 0; ii < 6; ++ii) {
      if (changed[ii] < 0) continue;
      bool dup = false;
      for (int jj = 0; jj < ii; ++jj) {
        if (changed[jj] == changed[ii]) { dup = true; break; }
      }
      if (dup) continue;
      // Determine if this edge should be used
      bool use = false;
      for (int jj = 0; jj < 6; ++jj) {
        if (changed[jj] == changed[ii] && should_use[jj]) { use = true; break; }
      }
      if (edge_used[changed[ii]] != use) {
        edge_used[changed[ii]] = use;
        refresh_incident(changed[ii]);
      }
    }

    if (current_objective > best_objective) {
      best_objective = current_objective;
      best_order = cur_order;
    }
    return true;
  }

  void solve() {
    build_board_graph();
    build_moves();
    build_initial_state(build_initial_order());

    double temp = temperature();
    double or_temp = or_temperature();
    while (utility::timer.elapsed_ms() < TIME_LIMIT_MS) {
      ++iterations;
      if ((iterations & 255) == 0) {
        temp = temperature();
        or_temp = or_temperature();
      }

      // Mix or-opt with 2x2 moves: ~50% or-opt, ~50% 2x2
      if ((rand_int() & 1) == 0) {
        try_or_opt(or_temp);
      } else {
        if (!active_moves.empty()) {
          const int mid = active_moves[rand_int() % active_moves.size()];
          apply_move(mid, temp);
        }
      }
    }
  }


  void output() const {
    for (int v : best_order) {
      cout << row_id[v] << ' ' << col_id[v] << '\n';
    }
  }

  void report() const {
    const long long score = (best_objective + V / 2) / V;
    cerr << "mode: sa-2x2-boundary\n";
    cerr << "iterations: " << iterations << '\n';
    cerr << "best score: " << score << '\n';
    cerr << "best objective: " << best_objective << '\n';
    cerr << "moves: " << moves.size() << '\n';
  }
};

constexpr int Solver::DR[8];
constexpr int Solver::DC[8];

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

  utility::timer.begin();

  Solver solver;
  solver.input();
  solver.solve();
  solver.report();
  solver.output();
  return 0;
}

Submission Info

Submission Time
Task A - King's Tour
User through
Language C++23 (GCC 15.2.0)
Score 46946068145
Code Size 31064 Byte
Status AC
Exec Time 2960 ms
Memory 26428 KiB

Judge Result

Set Name test_ALL
Score / Max Score 46946068145 / 100000000000
Status
AC × 100
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
Case Name Status Exec Time Memory
test_0000.txt AC 2957 ms 26312 KiB
test_0001.txt AC 2957 ms 26284 KiB
test_0002.txt AC 2959 ms 26252 KiB
test_0003.txt AC 2957 ms 26276 KiB
test_0004.txt AC 2959 ms 26196 KiB
test_0005.txt AC 2959 ms 26224 KiB
test_0006.txt AC 2959 ms 26188 KiB
test_0007.txt AC 2959 ms 26372 KiB
test_0008.txt AC 2957 ms 26136 KiB
test_0009.txt AC 2957 ms 26252 KiB
test_0010.txt AC 2959 ms 26256 KiB
test_0011.txt AC 2959 ms 26284 KiB
test_0012.txt AC 2957 ms 26196 KiB
test_0013.txt AC 2957 ms 26188 KiB
test_0014.txt AC 2957 ms 26364 KiB
test_0015.txt AC 2957 ms 26176 KiB
test_0016.txt AC 2957 ms 26428 KiB
test_0017.txt AC 2957 ms 26224 KiB
test_0018.txt AC 2959 ms 26224 KiB
test_0019.txt AC 2960 ms 26200 KiB
test_0020.txt AC 2959 ms 26192 KiB
test_0021.txt AC 2957 ms 26268 KiB
test_0022.txt AC 2960 ms 26312 KiB
test_0023.txt AC 2957 ms 26280 KiB
test_0024.txt AC 2959 ms 26232 KiB
test_0025.txt AC 2959 ms 26216 KiB
test_0026.txt AC 2959 ms 26084 KiB
test_0027.txt AC 2957 ms 26284 KiB
test_0028.txt AC 2957 ms 26204 KiB
test_0029.txt AC 2959 ms 26208 KiB
test_0030.txt AC 2957 ms 26196 KiB
test_0031.txt AC 2959 ms 26064 KiB
test_0032.txt AC 2959 ms 26360 KiB
test_0033.txt AC 2959 ms 26284 KiB
test_0034.txt AC 2959 ms 26216 KiB
test_0035.txt AC 2957 ms 26192 KiB
test_0036.txt AC 2959 ms 26192 KiB
test_0037.txt AC 2959 ms 26276 KiB
test_0038.txt AC 2957 ms 26336 KiB
test_0039.txt AC 2959 ms 26360 KiB
test_0040.txt AC 2959 ms 26196 KiB
test_0041.txt AC 2959 ms 26112 KiB
test_0042.txt AC 2957 ms 26224 KiB
test_0043.txt AC 2957 ms 26188 KiB
test_0044.txt AC 2957 ms 26216 KiB
test_0045.txt AC 2959 ms 26264 KiB
test_0046.txt AC 2959 ms 26084 KiB
test_0047.txt AC 2957 ms 26080 KiB
test_0048.txt AC 2957 ms 26252 KiB
test_0049.txt AC 2957 ms 26200 KiB
test_0050.txt AC 2957 ms 26312 KiB
test_0051.txt AC 2957 ms 26340 KiB
test_0052.txt AC 2957 ms 26256 KiB
test_0053.txt AC 2957 ms 26364 KiB
test_0054.txt AC 2959 ms 26252 KiB
test_0055.txt AC 2959 ms 26212 KiB
test_0056.txt AC 2959 ms 26232 KiB
test_0057.txt AC 2957 ms 26200 KiB
test_0058.txt AC 2959 ms 26204 KiB
test_0059.txt AC 2959 ms 26252 KiB
test_0060.txt AC 2959 ms 26144 KiB
test_0061.txt AC 2959 ms 26332 KiB
test_0062.txt AC 2957 ms 26276 KiB
test_0063.txt AC 2957 ms 26240 KiB
test_0064.txt AC 2959 ms 26116 KiB
test_0065.txt AC 2959 ms 26316 KiB
test_0066.txt AC 2959 ms 26268 KiB
test_0067.txt AC 2957 ms 26196 KiB
test_0068.txt AC 2957 ms 26292 KiB
test_0069.txt AC 2957 ms 26304 KiB
test_0070.txt AC 2959 ms 26264 KiB
test_0071.txt AC 2957 ms 26268 KiB
test_0072.txt AC 2957 ms 26196 KiB
test_0073.txt AC 2959 ms 26284 KiB
test_0074.txt AC 2959 ms 26332 KiB
test_0075.txt AC 2959 ms 26348 KiB
test_0076.txt AC 2959 ms 26272 KiB
test_0077.txt AC 2959 ms 26360 KiB
test_0078.txt AC 2957 ms 26188 KiB
test_0079.txt AC 2957 ms 26312 KiB
test_0080.txt AC 2957 ms 26208 KiB
test_0081.txt AC 2959 ms 26276 KiB
test_0082.txt AC 2959 ms 26360 KiB
test_0083.txt AC 2957 ms 26268 KiB
test_0084.txt AC 2957 ms 26332 KiB
test_0085.txt AC 2957 ms 26264 KiB
test_0086.txt AC 2957 ms 26200 KiB
test_0087.txt AC 2957 ms 26340 KiB
test_0088.txt AC 2957 ms 26308 KiB
test_0089.txt AC 2957 ms 26344 KiB
test_0090.txt AC 2959 ms 26276 KiB
test_0091.txt AC 2959 ms 26196 KiB
test_0092.txt AC 2957 ms 26252 KiB
test_0093.txt AC 2959 ms 26236 KiB
test_0094.txt AC 2957 ms 26260 KiB
test_0095.txt AC 2957 ms 26368 KiB
test_0096.txt AC 2957 ms 26308 KiB
test_0097.txt AC 2959 ms 26200 KiB
test_0098.txt AC 2957 ms 26272 KiB
test_0099.txt AC 2959 ms 26216 KiB