Submission #72327043
Source Code Expand
#include <bits/stdc++.h>
using namespace std;
static constexpr int N = 20;
static constexpr int MAX_OPS = 2 * N * N * N; // 16000
// ---------------- RNG ----------------
struct XorShift64 {
uint64_t x = 88172645463393265ull;
inline uint64_t next_u64() {
x ^= x << 7;
x ^= x >> 9;
return x;
}
inline uint32_t next_u32() { return (uint32_t)next_u64(); }
inline int next_int(int lo, int hi) {
return lo + (int)(next_u32() % (uint32_t)(hi - lo + 1));
}
inline double next_double01() {
return (next_u64() >> 11) * (1.0 / 9007199254740992.0);
}
};
struct Timer {
using clk = chrono::steady_clock;
clk::time_point st;
Timer() : st(clk::now()) {}
inline double sec() const {
return chrono::duration<double>(clk::now() - st).count();
}
};
struct Pt {
int r, c;
};
static inline int manhattan(const Pt &a, const Pt &b) {
return abs(a.r - b.r) + abs(a.c - b.c);
}
static inline long long penalty_ops(long long total_ops) {
if (total_ops <= MAX_OPS)
return 0LL;
return (total_ops - MAX_OPS) * 1000000LL;
}
// ---------------- State ----------------
struct State {
int M = 0;
vector<Pt> p0, p1;
int root; // = M
vector<int> parent; // size M+1
vector<vector<int>> ch; // size M+1
vector<uint8_t> flip; // size M
long long cost = (1LL << 60);
vector<int> seq;
vector<Pt> visit_pts;
long long K_move = 0;
vector<int> open_pos, close_pos;
bool cache_valid = false;
void build_sequence() {
seq.clear();
seq.reserve(2 * M);
struct Frame {
int u, it;
bool opened;
};
vector<Frame> st;
st.push_back({root, 0, true});
while (!st.empty()) {
auto &fr = st.back();
int u = fr.u;
if (u != root && !fr.opened) {
seq.push_back(+u);
fr.opened = true;
}
if (fr.it < (int)ch[u].size()) {
int v = ch[u][fr.it++];
st.push_back({v, 0, false});
} else {
if (u != root)
seq.push_back(-u - 1);
st.pop_back();
}
}
}
void build_visit_points() {
visit_pts.clear();
visit_pts.reserve(2 * M);
for (int e : seq) {
if (e >= 0) {
int u = e;
visit_pts.push_back(!flip[u] ? p0[u] : p1[u]);
} else {
int u = -e - 1;
visit_pts.push_back(!flip[u] ? p1[u] : p0[u]);
}
}
}
void build_open_close_pos() {
open_pos.assign(M, -1);
close_pos.assign(M, -1);
for (int i = 0; i < (int)seq.size(); i++) {
int e = seq[i];
if (e >= 0)
open_pos[e] = i;
else
close_pos[-e - 1] = i;
}
}
long long compute_K_move() const {
Pt cur{0, 0};
long long K = 0;
for (const auto &p : visit_pts) {
K += manhattan(cur, p);
cur = p;
}
return K;
}
long long eval_cost_full() {
build_sequence();
if ((int)seq.size() != 2 * M) {
cost = (1LL << 60);
cache_valid = false;
return cost;
}
build_visit_points();
build_open_close_pos();
K_move = compute_K_move();
long long Zcnt = 2LL * M; // baseline: take only
cost = K_move + penalty_ops(K_move + Zcnt);
cache_valid = true;
return cost;
}
inline long long edge_dist_idx(int idx) const {
if (idx == -1)
return manhattan(Pt{0, 0}, visit_pts[0]);
return manhattan(visit_pts[idx], visit_pts[idx + 1]);
}
void apply_flip_delta(int u) {
int i = open_pos[u];
int j = close_pos[u];
if (i < 0 || j < 0 || i == j) {
flip[u] ^= 1;
(void)eval_cost_full();
return;
}
if (i > j)
swap(i, j);
const int L = (int)visit_pts.size();
int cand[4] = {i - 1, i, j - 1, j};
int edges[4];
int ec = 0;
for (int t = 0; t < 4; t++) {
int k = cand[t];
if (k < -1)
continue;
if (k > L - 2)
continue;
edges[ec++] = k;
}
sort(edges, edges + ec);
ec = (int)(unique(edges, edges + ec) - edges);
long long before = 0;
for (int t = 0; t < ec; t++)
before += edge_dist_idx(edges[t]);
flip[u] ^= 1;
swap(visit_pts[i], visit_pts[j]);
long long after = 0;
for (int t = 0; t < ec; t++)
after += edge_dist_idx(edges[t]);
K_move += (after - before);
long long Zcnt = 2LL * M;
cost = K_move + penalty_ops(K_move + Zcnt);
}
bool is_ancestor(int a, int b) const {
int x = b;
while (x != -1 && x != root) {
if (x == a)
return true;
x = parent[x];
}
return (a == root);
}
int index_in_children(int par, int u) const {
const auto &v = ch[par];
for (int i = 0; i < (int)v.size(); i++)
if (v[i] == u)
return i;
return -1;
}
void erase_child(int par, int idx) {
auto &v = ch[par];
v.erase(v.begin() + idx);
}
void insert_child(int par, int idx, int u) {
auto &v = ch[par];
v.insert(v.begin() + idx, u);
}
static inline long long distPt(const Pt &a, const Pt &b) {
return (long long)abs(a.r - b.r) + (long long)abs(a.c - b.c);
}
static inline Pt START_PT() { return Pt{0, 0}; }
// vp の「辺」を加算(左端は START から)
static inline long long edge_between(const vector<Pt> &vp, int left_idx) {
// left_idx = -1 means START -> vp[0]
if (left_idx == -1)
return distPt(START_PT(), vp[0]);
return distPt(vp[left_idx], vp[left_idx + 1]);
}
void rebuild_open_close_pos_from_seq() {
open_pos.assign(M, -1);
close_pos.assign(M, -1);
for (int i = 0; i < (int)seq.size(); i++) {
int e = seq[i];
if (e >= 0)
open_pos[e] = i;
else
close_pos[-e - 1] = i;
}
}
int compute_insert_pos_in_seq(int new_par, int new_idx) const {
// NOTE: ch[new_par] は「変更後(u を挿入済み)」の状態を想定してOK。
// ここでは new_idx の位置に u が入っている前提ではなく、
// 「u を挿入する位置 new_idx」を受け取る想定で書くのが扱いやすい。
// なので、呼び出し側で "挿入前の new_idx" を渡すのが安全。
if (new_par == root) {
// root は open/close が seq に存在しない
if (new_idx >= (int)ch[root].size())
return (int)seq.size();
int v = ch[root][new_idx];
return open_pos[v]; // v の subtree の先頭
} else {
if (new_idx >= (int)ch[new_par].size()) {
// parent の close の直前が末尾
return close_pos[new_par];
} else {
int v = ch[new_par][new_idx];
return open_pos[v];
}
}
}
bool apply_repar_fast(int u, int old_par, int old_idx, int new_par,
int new_idx) {
// 前提: cache_valid=true で seq/visit_pts/open_pos/close_pos/K_move
// が正しいこと
int l = open_pos[u];
int r = close_pos[u];
if (l < 0 || r < 0 || l >= r)
return false;
// ---- old boundary points (before cut) ----
Pt A = (l == 0 ? START_PT() : visit_pts[l - 1]);
Pt B = visit_pts[l];
Pt C = visit_pts[r];
bool hasD = (r + 1 < (int)visit_pts.size());
Pt D = hasD ? visit_pts[r + 1] : Pt{-1, -1};
long long K_before = K_move;
// ---- cut segment out from seq & visit_pts ----
const int segLen = r - l + 1;
vector<int> seg_seq(seq.begin() + l, seq.begin() + r + 1);
vector<Pt> seg_vp(visit_pts.begin() + l, visit_pts.begin() + r + 1);
seq.erase(seq.begin() + l, seq.begin() + r + 1);
visit_pts.erase(visit_pts.begin() + l, visit_pts.begin() + r + 1);
// ここで open/close が古いので作り直す(400要素)
rebuild_open_close_pos_from_seq();
// ---- compute insertion position in the "cut" sequence ----
// 重要:new_idx は「挿入前」を渡すべき(呼び出し側で調整)
int ins = compute_insert_pos_in_seq(new_par, new_idx);
// ins は [0..seq.size()] の範囲
// insertion boundary in cut-state
Pt P = (ins == 0 ? START_PT() : visit_pts[ins - 1]);
bool hasQ = (ins < (int)visit_pts.size());
Pt Q = hasQ ? visit_pts[ins] : Pt{-1, -1};
// ---- splice insert segment ----
seq.insert(seq.begin() + ins, seg_seq.begin(), seg_seq.end());
visit_pts.insert(visit_pts.begin() + ins, seg_vp.begin(), seg_vp.end());
// open/close を再構築(400要素)
rebuild_open_close_pos_from_seq();
// ---- K_move delta (O(1) edges) ----
// Old edges removed:
// A-B always exists
// C-D exists only if hasD
// After cut, at old location A connects to D (if hasD)
// In insertion place, edge P-Q existed only if hasQ
// After insertion, edges P-B and C-Q (if hasQ)
long long K = K_before;
// remove old boundary edges
K -= distPt(A, B);
if (hasD)
K -= distPt(C, D);
// add edge bridging cut location
if (hasD)
K += distPt(A, D);
// remove edge at insertion gap
if (hasQ)
K -= distPt(P, Q);
// add edges with inserted segment
K += distPt(P, B);
if (hasQ)
K += distPt(C, Q);
K_move = K;
// ---- cost update ----
long long Zcnt = 2LL * M;
cost = K_move + penalty_ops(K_move + Zcnt);
cache_valid = true;
return true;
} // State 内に追加(helper):
int insert_pos_by_before_v(int par_ins, int before_v) const {
// par_ins: insert into this parent
// before_v: the child that should come right after u (insert before it). -1
// means append at end.
if (before_v != -1) {
// insert before that subtree's first token
return open_pos[before_v];
}
// append
if (par_ins == root)
return (int)seq.size();
return close_pos[par_ins]; // insert before parent's close
}
// State 内に追加(REPAR fast 本体)
bool apply_repar_fast_beforev(int u, int par_ins, int before_v) {
// 前提: cache_valid=true, seq/visit_pts/open_pos/close_pos/K_move が整合
int l = open_pos[u];
int r = close_pos[u];
if (l < 0 || r < 0 || l >= r)
return false;
// --- old boundary (cut) ---
Pt A = (l == 0 ? Pt{0, 0} : visit_pts[l - 1]);
Pt B = visit_pts[l];
Pt C = visit_pts[r];
bool hasD = (r + 1 < (int)visit_pts.size());
Pt D = hasD ? visit_pts[r + 1] : Pt{-1, -1};
long long K_before = K_move;
// cut segment
vector<int> seg_seq(seq.begin() + l, seq.begin() + r + 1);
vector<Pt> seg_vp(visit_pts.begin() + l, visit_pts.begin() + r + 1);
seq.erase(seq.begin() + l, seq.begin() + r + 1);
visit_pts.erase(visit_pts.begin() + l, visit_pts.begin() + r + 1);
// rebuild open/close for "cut" state
rebuild_open_close_pos_from_seq();
// compute insertion index in cut-state
int ins = insert_pos_by_before_v(par_ins, before_v);
// insertion boundary
Pt P = (ins == 0 ? Pt{0, 0} : visit_pts[ins - 1]);
bool hasQ = (ins < (int)visit_pts.size());
Pt Q = hasQ ? visit_pts[ins] : Pt{-1, -1};
// splice insert segment
seq.insert(seq.begin() + ins, seg_seq.begin(), seg_seq.end());
visit_pts.insert(visit_pts.begin() + ins, seg_vp.begin(), seg_vp.end());
// rebuild open/close for final state
rebuild_open_close_pos_from_seq();
// --- K_move delta (O(1)) ---
long long K = K_before;
// remove A-B and C-D
K -= manhattan(A, B);
if (hasD)
K -= manhattan(C, D);
// add A-D bridge
if (hasD)
K += manhattan(A, D);
// remove P-Q gap edge
if (hasQ)
K -= manhattan(P, Q);
// add P-B and C-Q
K += manhattan(P, B);
if (hasQ)
K += manhattan(C, Q);
K_move = K;
long long Zcnt = 2LL * M;
cost = K_move + penalty_ops(K_move + Zcnt);
cache_valid = true;
return true;
}
};
// ---------------- SA Moves ----------------
enum MoveType { REPAR = 0, SWAP_SIB = 1, FLIP = 2 };
struct Move {
MoveType type;
int u = -1;
int old_par = -1, old_idx = -1;
int new_par = -1, new_idx = -1;
// REPAR fast 用:
int old_before_v = -1; // u を元に戻す時「この子の前」に挿入(-1なら末尾)
int new_before_v = -1; // u を新しい親へ移す時「この子の前」に挿入
int par = -1;
int i = -1, j = -1;
};
static inline double acceptance_prob(long long delta, double T) {
return exp(-(double)delta / T);
}
// ---------------- Path helpers ----------------
static inline void enumerate_path_cells_vh(const Pt &from, const Pt &to,
vector<Pt> &cells) {
cells.clear();
Pt cur = from;
int dr = to.r - cur.r;
int dc = to.c - cur.c;
if (dr < 0)
for (int k = 0; k < -dr; k++) {
cur.r--;
cells.push_back(cur);
}
else
for (int k = 0; k < dr; k++) {
cur.r++;
cells.push_back(cur);
}
if (dc < 0)
for (int k = 0; k < -dc; k++) {
cur.c--;
cells.push_back(cur);
}
else
for (int k = 0; k < dc; k++) {
cur.c++;
cells.push_back(cur);
}
}
static inline char step_dir(const Pt &a, const Pt &b) {
if (b.r == a.r - 1 && b.c == a.c)
return 'U';
if (b.r == a.r + 1 && b.c == a.c)
return 'D';
if (b.c == a.c - 1 && b.r == a.r)
return 'L';
if (b.c == a.c + 1 && b.r == a.r)
return 'R';
return '?';
}
// ---------------- Postprocess relocation (NO detour) ----------------
struct Action {
int cell_idx; // index in cells[]
char op; // 'Z' take, 'X' place
};
struct PostPlan {
vector<Pt> visit_pts;
vector<vector<Action>> mid_actions; // per segment t
long long Km = 0;
long long opcnt = 0; // total ops (moves + Z/X)
long long cost = (1LL << 60);
};
static inline long long compute_move_from_visit_pts(const vector<Pt> &vp) {
Pt cur{0, 0};
long long Km = 0;
for (auto &p : vp) {
Km += manhattan(cur, p);
cur = p;
}
return Km;
}
static inline long long delta_move_change_at(const vector<Pt> &vp, int t,
const Pt &X, const Pt &Y) {
const int E = (int)vp.size();
Pt prev = (t == 0 ? Pt{0, 0} : vp[t - 1]);
long long before = manhattan(prev, X);
long long after = manhattan(prev, Y);
if (t < E - 1) {
before += manhattan(X, vp[t + 1]);
after += manhattan(Y, vp[t + 1]);
}
return after - before;
}
static inline void remove_event(vector<int> &lst, int tf) {
auto it = lower_bound(lst.begin(), lst.end(), tf);
if (it != lst.end() && *it == tf)
lst.erase(it);
}
static PostPlan postprocess_relocate_no_detour(const State &best,
const vector<vector<int>> &a) {
const int M = best.M;
const int E = 2 * M;
// board stores original numbers; -1 empty
vector<vector<int>> board(N, vector<int>(N, -1));
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
board[i][j] = a[i][j];
vector<Pt> vp = best.visit_pts;
vector<vector<vector<int>>> events_at(N, vector<vector<int>>(N));
for (int t = 0; t < E; t++)
events_at[vp[t].r][vp[t].c].push_back(t);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
sort(events_at[i][j].begin(), events_at[i][j].end());
vector<char> relocated(E, 0);
vector<vector<Action>> mid(E);
long long Km_cur = compute_move_from_visit_pts(vp);
// baseline action count: moves(Km_cur) + takes(E)
long long acts_cur = Km_cur + E;
long long cost_cur = Km_cur + penalty_ops(acts_cur);
vector<int> stack;
Pt curPos{0, 0};
for (int t = 0; t < E; t++) {
vector<Pt> cells;
enumerate_path_cells_vh(curPos, vp[t], cells);
const int L = (int)cells.size(); // includes destination at index L-1 if L>0
// empty positions on segment (current time)
vector<int> empty_idx;
empty_idx.reserve(L);
for (int i = 0; i < L; i++) {
Pt p = cells[i];
if (board[p.r][p.c] == -1)
empty_idx.push_back(i);
}
bool applied = false;
// IMPORTANT: do NOT pick at destination cell (index L-1), because we will
// also 'Z' at destination for event t.
for (int i = 0; i < L - 1 && !applied; i++) {
Pt X = cells[i];
int val = board[X.r][X.c];
if (val == -1)
continue;
// safety: picking must not cancel immediately (card disappears -> can't
// place)
if (!stack.empty() && stack.back() == val)
continue;
// find future event tf>t targeting X
auto &lst = events_at[X.r][X.c];
auto it = upper_bound(lst.begin(), lst.end(), t);
int tf = -1;
for (; it != lst.end(); ++it) {
int cand_tf = *it;
if (!relocated[cand_tf] && vp[cand_tf].r == X.r &&
vp[cand_tf].c == X.c) {
tf = cand_tf;
break;
}
}
if (tf < 0)
continue;
// choose placement Y among empty cells after i (can be destination too;
// that's fine)
long long best_cost = cost_cur;
int best_j = -1;
long long best_dm = 0;
for (int jidx : empty_idx) {
if (jidx <= i)
continue;
Pt Y = cells[jidx];
long long dm = delta_move_change_at(vp, tf, X, Y);
long long Km_new = Km_cur + dm;
// extra actions: +2 (Z pick + X place), no extra moves
long long acts_new = (Km_new + E) + 2;
long long cost_new = Km_new + penalty_ops(acts_new);
if (cost_new < best_cost) {
best_cost = cost_new;
best_j = jidx;
best_dm = dm;
}
}
if (best_j >= 0) {
Pt Y = cells[best_j];
// record mid actions
mid[t].push_back({i, 'Z'}); // take at X
mid[t].push_back({best_j, 'X'}); // place at Y
sort(mid[t].begin(), mid[t].end(),
[](const Action &a, const Action &b) {
if (a.cell_idx != b.cell_idx)
return a.cell_idx < b.cell_idx;
return a.op < b.op;
});
// apply relocation on board
board[X.r][X.c] = -1;
board[Y.r][Y.c] = val;
// update mapping
remove_event(events_at[X.r][X.c], tf);
events_at[Y.r][Y.c].insert(lower_bound(events_at[Y.r][Y.c].begin(),
events_at[Y.r][Y.c].end(), tf),
tf);
vp[tf] = Y;
relocated[tf] = 1;
// update costs
Km_cur += best_dm;
// actions +2
acts_cur = (Km_cur + E) + 2 * 1; // we only keep it accurate for greedy
// by bumping via +2 each time
// But we might have multiple relocations; better accumulate:
// Let's recompute actions later; for acceptance we can track count:
// We'll keep a counter:
cost_cur = best_cost;
applied = true;
}
}
// ---- simulate movement and execute mid actions (Z take / X place) ----
sort(mid[t].begin(), mid[t].end(), [](const Action &a, const Action &b) {
if (a.cell_idx != b.cell_idx)
return a.cell_idx < b.cell_idx;
return a.op < b.op;
});
int aptr = 0;
for (int i = 0; i < L; i++) {
curPos = cells[i];
while (aptr < (int)mid[t].size() && mid[t][aptr].cell_idx == i) {
char op = mid[t][aptr].op;
if (op == 'Z') {
// take: must have card
int v = board[curPos.r][curPos.c];
if (v == -1) {
// invalid; ignore (but should not happen)
} else {
board[curPos.r][curPos.c] = -1;
if (!stack.empty() && stack.back() == v)
stack.pop_back();
else
stack.push_back(v);
}
} else { // 'X' place
if (board[curPos.r][curPos.c] != -1) {
// invalid; ignore (but should not happen)
} else if (!stack.empty()) {
int top = stack.back();
stack.pop_back();
board[curPos.r][curPos.c] = top;
}
}
aptr++;
}
}
// destination event t: Take with 'Z' (must have card ideally)
curPos = vp[t];
int v = board[curPos.r][curPos.c];
if (v != -1) {
board[curPos.r][curPos.c] = -1;
if (!stack.empty() && stack.back() == v)
stack.pop_back();
else
stack.push_back(v);
} else {
// if empty here, outputting 'Z' would cause "Take from empty cell" in
// real judge so we should avoid such plans by not allowing picks at
// destination. As a last-resort safety, we can "do nothing" in
// simulation. (Output will still do 'Z', but this case should not occur
// now.)
}
}
// final exact stats
PostPlan plan;
plan.visit_pts = std::move(vp);
plan.mid_actions = std::move(mid);
plan.Km = compute_move_from_visit_pts(plan.visit_pts);
long long extra = 0;
for (int t = 0; t < E; t++)
extra += (long long)plan.mid_actions[t].size();
long long total_actions =
plan.Km + E + extra; // moves + destination-takes + mid(Z/X)
plan.opcnt = total_actions;
plan.cost = plan.Km + penalty_ops(total_actions);
// If not improved, revert
{
long long origKm = compute_move_from_visit_pts(best.visit_pts);
long long origActions = origKm + E;
long long origCost = origKm + penalty_ops(origActions);
if (plan.cost >= origCost) {
plan.visit_pts = best.visit_pts;
plan.mid_actions.assign(E, {});
plan.Km = origKm;
plan.opcnt = origActions;
plan.cost = origCost;
}
}
return plan;
}
// Output ops with mid Z/X insertions + destination Z
static string
build_ops_with_insertions(const vector<Pt> &vp,
const vector<vector<Action>> &mid_actions) {
const int E = (int)vp.size();
string ops;
ops.reserve(30000);
Pt cur{0, 0};
for (int t = 0; t < E; t++) {
vector<Pt> cells;
enumerate_path_cells_vh(cur, vp[t], cells);
int L = (int)cells.size();
vector<Action> acts = mid_actions[t];
sort(acts.begin(), acts.end(), [](const Action &a, const Action &b) {
if (a.cell_idx != b.cell_idx)
return a.cell_idx < b.cell_idx;
return a.op < b.op;
});
int aptr = 0;
Pt prev = cur;
for (int i = 0; i < L; i++) {
Pt nxt = cells[i];
ops.push_back(step_dir(prev, nxt));
prev = nxt;
while (aptr < (int)acts.size() && acts[aptr].cell_idx == i) {
ops.push_back(acts[aptr].op); // 'Z' take or 'X' place
aptr++;
}
}
// destination action: Take with 'Z'
ops.push_back('Z');
cur = vp[t];
}
if ((int)ops.size() > MAX_OPS)
ops.resize(MAX_OPS);
return ops;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int Nin;
cin >> Nin;
vector<vector<int>> a(N, vector<int>(N));
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
cin >> a[i][j];
// Map values to 0..M-1
unordered_map<int, int> id;
id.reserve(N * N * 2);
vector<array<Pt, 2>> pos;
vector<int> cnt;
auto get_id = [&](int val) -> int {
auto it = id.find(val);
if (it != id.end())
return it->second;
int nid = (int)id.size();
id.emplace(val, nid);
pos.push_back({Pt{-1, -1}, Pt{-1, -1}});
cnt.push_back(0);
return nid;
};
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++) {
int v = a[i][j];
int k = get_id(v);
if (cnt[k] < 2) {
pos[k][cnt[k]] = Pt{i, j};
cnt[k]++;
}
}
int M = (int)id.size();
State st;
st.M = M;
st.root = M;
st.p0.resize(M);
st.p1.resize(M);
for (int k = 0; k < M; k++) {
st.p0[k] = pos[k][0];
st.p1[k] = pos[k][1];
}
st.parent.assign(M + 1, -1);
st.ch.assign(M + 1, {});
st.flip.assign(M, 0);
// Initial forest: all nodes under root, ordered by distance from start
vector<int> ord(M);
iota(ord.begin(), ord.end(), 0);
Pt start{0, 0};
sort(ord.begin(), ord.end(), [&](int u, int v) {
int du = min(manhattan(start, st.p0[u]), manhattan(start, st.p1[u]));
int dv = min(manhattan(start, st.p0[v]), manhattan(start, st.p1[v]));
return du < dv;
});
st.ch[st.root] = ord;
for (int u = 0; u < M; u++)
st.parent[u] = st.root;
st.parent[st.root] = -1;
for (int u = 0; u < M; u++) {
int d0 = manhattan(start, st.p0[u]);
int d1 = manhattan(start, st.p1[u]);
st.flip[u] = (d1 < d0) ? 1 : 0;
}
long long cur = st.eval_cost_full();
State best = st;
long long best_cost = cur;
XorShift64 rng;
Timer timer;
const double TIME_LIMIT = 1.95;
const double T0 = 0.0;
const double T1 = 0.0;
auto temperature = [&](double t) -> double {
double x = t / TIME_LIMIT;
x = max(0.0, min(1.0, x));
return T0 * pow(T1 / T0, x);
};
auto sample_move_type = [&]() -> MoveType {
int r = rng.next_int(0, 99);
if (r < 10)
return FLIP;
if (r < 20)
return SWAP_SIB;
return REPAR;
};
auto apply_move_structural = [&](State &S, Move &mv) -> bool {
if (mv.type == SWAP_SIB) {
for (int tries = 0; tries < 1; tries++) {
int par = rng.next_int(0, S.M);
if ((int)S.ch[par].size() >= 2) {
int sz = (int)S.ch[par].size();
int i = rng.next_int(0, sz - 1);
int j = rng.next_int(0, sz - 1);
if (i == j)
continue;
mv.par = par;
mv.i = i;
mv.j = j;
swap(S.ch[par][i], S.ch[par][j]);
S.cache_valid = false; // ここは従来通り full eval に任せる
return true;
}
}
return false;
}
// -------- REPAR (FAST) --------
// cache を有効化(seq/visit_pts/open/close/K_move 必須)
if (!S.cache_valid)
(void)S.eval_cost_full();
for (int tries = 0; tries < 1; tries++) {
int u = rng.next_int(0, S.M - 1);
int old_par = S.parent[u];
if (old_par < 0)
continue;
int new_par = rng.next_int(0, S.M);
if (new_par == u)
continue;
if (new_par == old_par)
continue; // ←簡単化:同一親内の移動は別moveでやる(まずはここを禁止)
if (new_par != S.root && S.is_ancestor(u, new_par))
continue;
int old_idx = S.index_in_children(old_par, u);
if (old_idx < 0)
continue;
// --- “before_v” を記録 ---
// old: 元に戻すとき old_idx の位置に戻したい
// => u の直後に来ていた子(old_idx+1)を覚える(末尾なら -1)
int old_before_v = -1;
if (old_idx + 1 < (int)S.ch[old_par].size())
old_before_v = S.ch[old_par][old_idx + 1];
// new: new_par の new_pos に入れたい
// => その位置に元々いた子を before_v とする(末尾なら -1)
int new_pos = rng.next_int(0, (int)S.ch[new_par].size());
int new_before_v = -1;
if (new_pos < (int)S.ch[new_par].size())
new_before_v = S.ch[new_par][new_pos];
// --- 木を変更(u を old_par から外して new_par に入れる)---
mv.u = u;
mv.old_par = old_par;
mv.old_idx = old_idx;
mv.new_par = new_par;
mv.new_idx = new_pos;
mv.old_before_v = old_before_v;
mv.new_before_v = new_before_v;
S.erase_child(old_par, old_idx);
S.insert_child(new_par, new_pos, u);
S.parent[u] = new_par;
// --- seq/visit_pts/K_move を fast splice で更新 ---
bool ok = S.apply_repar_fast_beforev(u, new_par, new_before_v);
if (!ok) {
// 失敗時は木を戻して別試行
int idx2 = S.index_in_children(new_par, u);
if (idx2 >= 0)
S.erase_child(new_par, idx2);
S.insert_child(old_par, old_idx, u);
S.parent[u] = old_par;
(void)S.eval_cost_full(); // 保険(ここが頻発するなら原因を潰す)
continue;
}
return true;
}
return false;
};
auto undo_move = [&](State &S, const Move &mv) {
if (mv.type == FLIP)
return;
if (mv.type == SWAP_SIB) {
swap(S.ch[mv.par][mv.i], S.ch[mv.par][mv.j]);
S.cache_valid = false;
return;
}
// -------- REPAR undo (FAST) --------
// cache が壊れている可能性があるなら直す
if (!S.cache_valid)
(void)S.eval_cost_full();
// 木を戻す: new_par から u を外して old_par に戻す
int cur_par = S.parent[mv.u]; // = mv.new_par のはず
int idx = S.index_in_children(cur_par, mv.u);
if (idx >= 0)
S.erase_child(cur_par, idx);
S.insert_child(mv.old_par, mv.old_idx, mv.u);
S.parent[mv.u] = mv.old_par;
// seq/visit_pts/K_move を “元の位置へ” fast splice で戻す
// old_before_v は「元の u の直後に来ていた子」。それが -1 なら末尾。
bool ok = S.apply_repar_fast_beforev(mv.u, mv.old_par, mv.old_before_v);
if (!ok) {
// 万一の保険(基本起きない想定)
(void)S.eval_cost_full();
}
};
while (true) {
double t = timer.sec();
if (t >= TIME_LIMIT)
break;
double T = temperature(t);
Move mv;
mv.type = sample_move_type();
if (mv.type == FLIP) {
if (!st.cache_valid)
cur = st.eval_cost_full();
mv.u = rng.next_int(0, st.M - 1);
long long before_cost = st.cost;
st.apply_flip_delta(mv.u);
long long nxt = st.cost;
long long delta = nxt - before_cost;
bool accept = false;
if (delta <= 0)
accept = true;
else {
if (delta > (long long)(50.0 * T))
accept = false;
else
accept = (rng.next_double01() < acceptance_prob(delta, T));
}
if (accept) {
cur = nxt;
if (cur < best_cost) {
best_cost = cur;
best = st;
}
} else {
st.apply_flip_delta(mv.u);
cur = st.cost;
}
} else {
// structural (SWAP_SIB / REPAR)
bool ok = apply_move_structural(st, mv);
if (!ok)
continue;
// ここで st.cost が最新のはず:
// - SWAP_SIB なら cache_valid=false なので full eval が必要
// - REPAR なら apply_repar_fast_beforev が cost 更新済みで
// cache_valid=true
long long before_cost = cur;
long long nxt;
if (!st.cache_valid) {
nxt = st.eval_cost_full(); // SWAP_SIB用
} else {
nxt = st.cost; // REPAR fast用
}
long long delta = nxt - before_cost;
bool accept = false;
if (delta <= 0)
accept = true;
else {
if (delta > (long long)(50.0 * T))
accept = false;
else
accept = (rng.next_double01() < acceptance_prob(delta, T));
}
if (accept) {
cur = nxt;
if (cur < best_cost) {
best_cost = cur;
best = st;
}
} else {
undo_move(st, mv);
// undo で cache_valid が true なら cost は更新済み
if (!st.cache_valid)
cur = st.eval_cost_full();
else
cur = st.cost;
}
}
}
best.eval_cost_full();
// Postprocess with mid Z/X insertions
PostPlan plan = postprocess_relocate_no_detour(best, a);
// Output
string ops = build_ops_with_insertions(plan.visit_pts, plan.mid_actions);
for (char c : ops)
cout << c << "\n";
return 0;
}
Submission Info
Submission Time
2026-01-10 17:15:11+0900
Task
A - Stack to Match Pairs
User
ohuton_labo
Language
C++23 (GCC 15.2.0)
Score
2227365
Code Size
32314 Byte
Status
AC
Exec Time
1952 ms
Memory
4072 KiB
Compile Error
./Main.cpp: In member function 'bool State::apply_repar_fast(int, int, int, int, int)':
./Main.cpp:281:15: warning: unused variable 'segLen' [-Wunused-variable]
281 | const int segLen = r - l + 1;
| ^~~~~~
./Main.cpp:262:36: warning: unused parameter 'old_par' [-Wunused-parameter]
262 | bool apply_repar_fast(int u, int old_par, int old_idx, int new_par,
| ~~~~^~~~~~~
./Main.cpp:262:49: warning: unused parameter 'old_idx' [-Wunused-parameter]
262 | bool apply_repar_fast(int u, int old_par, int old_idx, int new_par,
| ~~~~^~~~~~~
Judge Result
Set Name
test_ALL
Score / Max Score
2227365 / 2460000
Status
Set Name
Test Cases
test_ALL
test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt, test_0100.txt, test_0101.txt, test_0102.txt, test_0103.txt, test_0104.txt, test_0105.txt, test_0106.txt, test_0107.txt, test_0108.txt, test_0109.txt, test_0110.txt, test_0111.txt, test_0112.txt, test_0113.txt, test_0114.txt, test_0115.txt, test_0116.txt, test_0117.txt, test_0118.txt, test_0119.txt, test_0120.txt, test_0121.txt, test_0122.txt, test_0123.txt, test_0124.txt, test_0125.txt, test_0126.txt, test_0127.txt, test_0128.txt, test_0129.txt, test_0130.txt, test_0131.txt, test_0132.txt, test_0133.txt, test_0134.txt, test_0135.txt, test_0136.txt, test_0137.txt, test_0138.txt, test_0139.txt, test_0140.txt, test_0141.txt, test_0142.txt, test_0143.txt, test_0144.txt, test_0145.txt, test_0146.txt, test_0147.txt, test_0148.txt, test_0149.txt
Case Name
Status
Exec Time
Memory
test_0000.txt
AC
1951 ms
4032 KiB
test_0001.txt
AC
1951 ms
3876 KiB
test_0002.txt
AC
1951 ms
3940 KiB
test_0003.txt
AC
1951 ms
4040 KiB
test_0004.txt
AC
1951 ms
3760 KiB
test_0005.txt
AC
1951 ms
3956 KiB
test_0006.txt
AC
1951 ms
4016 KiB
test_0007.txt
AC
1951 ms
3992 KiB
test_0008.txt
AC
1951 ms
4068 KiB
test_0009.txt
AC
1951 ms
3888 KiB
test_0010.txt
AC
1951 ms
3956 KiB
test_0011.txt
AC
1951 ms
4068 KiB
test_0012.txt
AC
1951 ms
3888 KiB
test_0013.txt
AC
1951 ms
3804 KiB
test_0014.txt
AC
1951 ms
3896 KiB
test_0015.txt
AC
1951 ms
3932 KiB
test_0016.txt
AC
1951 ms
3888 KiB
test_0017.txt
AC
1951 ms
3904 KiB
test_0018.txt
AC
1951 ms
3888 KiB
test_0019.txt
AC
1951 ms
3888 KiB
test_0020.txt
AC
1951 ms
4032 KiB
test_0021.txt
AC
1951 ms
3920 KiB
test_0022.txt
AC
1951 ms
3924 KiB
test_0023.txt
AC
1951 ms
3932 KiB
test_0024.txt
AC
1951 ms
4068 KiB
test_0025.txt
AC
1951 ms
3980 KiB
test_0026.txt
AC
1951 ms
3932 KiB
test_0027.txt
AC
1951 ms
3760 KiB
test_0028.txt
AC
1951 ms
3924 KiB
test_0029.txt
AC
1951 ms
4044 KiB
test_0030.txt
AC
1951 ms
4032 KiB
test_0031.txt
AC
1951 ms
3956 KiB
test_0032.txt
AC
1951 ms
3868 KiB
test_0033.txt
AC
1951 ms
4044 KiB
test_0034.txt
AC
1951 ms
3932 KiB
test_0035.txt
AC
1951 ms
4004 KiB
test_0036.txt
AC
1951 ms
3992 KiB
test_0037.txt
AC
1951 ms
3992 KiB
test_0038.txt
AC
1951 ms
3908 KiB
test_0039.txt
AC
1951 ms
4052 KiB
test_0040.txt
AC
1951 ms
3884 KiB
test_0041.txt
AC
1951 ms
3784 KiB
test_0042.txt
AC
1951 ms
4044 KiB
test_0043.txt
AC
1952 ms
3944 KiB
test_0044.txt
AC
1951 ms
4016 KiB
test_0045.txt
AC
1951 ms
4032 KiB
test_0046.txt
AC
1951 ms
4052 KiB
test_0047.txt
AC
1951 ms
3976 KiB
test_0048.txt
AC
1951 ms
3932 KiB
test_0049.txt
AC
1951 ms
3992 KiB
test_0050.txt
AC
1951 ms
4040 KiB
test_0051.txt
AC
1951 ms
3956 KiB
test_0052.txt
AC
1951 ms
3888 KiB
test_0053.txt
AC
1951 ms
3888 KiB
test_0054.txt
AC
1951 ms
4020 KiB
test_0055.txt
AC
1951 ms
3888 KiB
test_0056.txt
AC
1951 ms
3888 KiB
test_0057.txt
AC
1952 ms
4040 KiB
test_0058.txt
AC
1951 ms
3920 KiB
test_0059.txt
AC
1951 ms
4044 KiB
test_0060.txt
AC
1951 ms
4068 KiB
test_0061.txt
AC
1951 ms
3932 KiB
test_0062.txt
AC
1951 ms
3956 KiB
test_0063.txt
AC
1951 ms
3884 KiB
test_0064.txt
AC
1951 ms
4044 KiB
test_0065.txt
AC
1951 ms
4068 KiB
test_0066.txt
AC
1951 ms
3992 KiB
test_0067.txt
AC
1951 ms
4040 KiB
test_0068.txt
AC
1951 ms
3944 KiB
test_0069.txt
AC
1951 ms
3956 KiB
test_0070.txt
AC
1951 ms
4072 KiB
test_0071.txt
AC
1951 ms
4044 KiB
test_0072.txt
AC
1951 ms
3920 KiB
test_0073.txt
AC
1951 ms
4068 KiB
test_0074.txt
AC
1951 ms
3944 KiB
test_0075.txt
AC
1951 ms
4068 KiB
test_0076.txt
AC
1951 ms
3784 KiB
test_0077.txt
AC
1951 ms
4068 KiB
test_0078.txt
AC
1951 ms
3992 KiB
test_0079.txt
AC
1951 ms
3956 KiB
test_0080.txt
AC
1951 ms
3888 KiB
test_0081.txt
AC
1951 ms
3956 KiB
test_0082.txt
AC
1951 ms
3888 KiB
test_0083.txt
AC
1951 ms
3748 KiB
test_0084.txt
AC
1951 ms
3920 KiB
test_0085.txt
AC
1951 ms
3888 KiB
test_0086.txt
AC
1951 ms
3944 KiB
test_0087.txt
AC
1951 ms
3876 KiB
test_0088.txt
AC
1951 ms
3932 KiB
test_0089.txt
AC
1952 ms
4052 KiB
test_0090.txt
AC
1951 ms
4072 KiB
test_0091.txt
AC
1951 ms
3932 KiB
test_0092.txt
AC
1951 ms
3876 KiB
test_0093.txt
AC
1951 ms
4040 KiB
test_0094.txt
AC
1951 ms
4052 KiB
test_0095.txt
AC
1951 ms
4052 KiB
test_0096.txt
AC
1951 ms
3876 KiB
test_0097.txt
AC
1951 ms
3876 KiB
test_0098.txt
AC
1951 ms
3992 KiB
test_0099.txt
AC
1951 ms
3992 KiB
test_0100.txt
AC
1951 ms
4068 KiB
test_0101.txt
AC
1951 ms
4016 KiB
test_0102.txt
AC
1951 ms
4040 KiB
test_0103.txt
AC
1951 ms
3828 KiB
test_0104.txt
AC
1951 ms
4016 KiB
test_0105.txt
AC
1951 ms
3888 KiB
test_0106.txt
AC
1951 ms
3896 KiB
test_0107.txt
AC
1951 ms
3784 KiB
test_0108.txt
AC
1951 ms
3760 KiB
test_0109.txt
AC
1951 ms
4044 KiB
test_0110.txt
AC
1951 ms
3956 KiB
test_0111.txt
AC
1951 ms
4068 KiB
test_0112.txt
AC
1951 ms
4024 KiB
test_0113.txt
AC
1951 ms
3876 KiB
test_0114.txt
AC
1951 ms
4016 KiB
test_0115.txt
AC
1951 ms
4040 KiB
test_0116.txt
AC
1951 ms
3760 KiB
test_0117.txt
AC
1951 ms
3876 KiB
test_0118.txt
AC
1951 ms
3876 KiB
test_0119.txt
AC
1951 ms
4044 KiB
test_0120.txt
AC
1951 ms
3940 KiB
test_0121.txt
AC
1951 ms
4016 KiB
test_0122.txt
AC
1951 ms
3888 KiB
test_0123.txt
AC
1951 ms
3888 KiB
test_0124.txt
AC
1951 ms
4020 KiB
test_0125.txt
AC
1951 ms
3888 KiB
test_0126.txt
AC
1951 ms
3992 KiB
test_0127.txt
AC
1951 ms
4044 KiB
test_0128.txt
AC
1951 ms
3904 KiB
test_0129.txt
AC
1951 ms
3932 KiB
test_0130.txt
AC
1951 ms
3956 KiB
test_0131.txt
AC
1951 ms
3944 KiB
test_0132.txt
AC
1951 ms
3920 KiB
test_0133.txt
AC
1951 ms
3944 KiB
test_0134.txt
AC
1951 ms
4040 KiB
test_0135.txt
AC
1951 ms
4040 KiB
test_0136.txt
AC
1951 ms
3992 KiB
test_0137.txt
AC
1951 ms
3944 KiB
test_0138.txt
AC
1951 ms
4068 KiB
test_0139.txt
AC
1951 ms
3868 KiB
test_0140.txt
AC
1951 ms
3804 KiB
test_0141.txt
AC
1951 ms
3888 KiB
test_0142.txt
AC
1951 ms
4016 KiB
test_0143.txt
AC
1951 ms
4072 KiB
test_0144.txt
AC
1951 ms
4032 KiB
test_0145.txt
AC
1951 ms
4016 KiB
test_0146.txt
AC
1951 ms
3944 KiB
test_0147.txt
AC
1952 ms
4016 KiB
test_0148.txt
AC
1951 ms
3904 KiB
test_0149.txt
AC
1951 ms
3888 KiB