結果

問題 No.5023 Airlines Optimization
コンテスト
ユーザー mtmr_s1
提出日時 2026-02-25 23:34:06
言語 C++17
(gcc 15.2.0 + boost 1.89.0)
コンパイル:
g++-15 -O2 -lm -std=c++17 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
AC  
実行時間 981 ms / 1,000 ms
コード長 21,379 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 4,355 ms
コンパイル使用メモリ 281,688 KB
実行使用メモリ 7,844 KB
スコア 48,851,442
最終ジャッジ日時 2026-02-25 23:36:58
合計ジャッジ時間 108,272 ms
ジャッジサーバーID
(参考情報)
judge7 / judge3
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 100
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

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

static constexpr int START_MIN = 6 * 60;
static constexpr int END_MIN = 21 * 60;
static constexpr int STEP_MIN = 5;
static constexpr int TICK_MAX = (END_MIN - START_MIN) / STEP_MIN; // 180
static constexpr int NEG_INF = -1;

// ---- Hyper parameters ----
static constexpr double HP_TOTAL_LIMIT_SEC = 0.98;
static constexpr double HP_GREEDY_STOP_MARGIN_SEC = 0.04;
static constexpr double HP_SA_MIN_REMAIN_SEC = 0.03;
static constexpr double HP_SA_STOP_MARGIN_SEC = 0.003;

static constexpr int HP_WINDOW_STEP = 6;   // 30 min
static constexpr int HP_WINDOW_SIZE = 24;  // 2 hours

static constexpr int HP_GREEDY_UPSTREAM_MAX = 8;
static constexpr int HP_PROXY_UPSTREAM_MAX = 8;

static constexpr int HP_CITY_SAMPLE_TOP = 20;
static constexpr int HP_CITY_PICK_TRIES = 24;
static constexpr double HP_CITY_TOP_PICK_PROB = 0.80;

static constexpr int HP_MOVE_CITY_CHANGE_PCT = 45;
static constexpr int HP_MOVE_SHIFT_PCT = 65;
static constexpr int HP_MOVE_INSERT_PCT = 85;
static constexpr double HP_SECOND_CITY_MUTATE_PROB = 0.20;

static constexpr array<int, 4> HP_SHIFT_OPTIONS = {-2, -1, 1, 2};

static constexpr double HP_SA_T0 = 5e11;
static constexpr double HP_SA_T1 = 5e9;
static constexpr double HP_PREFILTER_TEMP_SCALE = 0.70;
static constexpr double HP_EXP_FLOOR = -50.0;
static constexpr long long HP_PROXY_POTENTIAL_DIV = 16;

struct City {
  int x, y;
  long long w;
};

struct Flight {
  int a, b; // 0-based city
  int s, t; // tick [0..180]
};

struct Candidate {
  bool valid = false;
  int b = -1;
  int t = -1;
  int dur = -1;
  long long delta = LLONG_MIN;
  long long potential = LLONG_MIN;
  long long pop = LLONG_MIN;
};

struct EvalResult {
  long long score = 0;
  vector<Flight> desc;
  vector<int> latest;
};

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

static int time_to_idx(const string &hhmm) {
  int hh = stoi(hhmm.substr(0, 2));
  int mm = stoi(hhmm.substr(3, 2));
  int mins = hh * 60 + mm;
  return (mins - START_MIN) / STEP_MIN;
}

static string idx_to_time(int idx) {
  int mins = START_MIN + idx * STEP_MIN;
  int hh = mins / 60;
  int mm = mins % 60;
  ostringstream oss;
  oss << setw(2) << setfill('0') << hh << ":" << setw(2) << setfill('0') << mm;
  return oss.str();
}

static int duration_ticks(const City &c1, const City &c2) {
  double dx = double(c1.x - c2.x);
  double dy = double(c1.y - c2.y);
  double dist = sqrt(dx * dx + dy * dy);
  double raw = 60.0 * dist / 800.0 + 40.0;
  int rounded = int(ceil(raw / 5.0 - 1e-12) * 5.0);
  return rounded / 5;
}

static inline int latest_idx(int n, int dcount, int dest, int dk, int orig) {
  return ((dest * dcount) + dk) * n + orig;
}

static void compute_latest_all_sorted(
    int n,
    const vector<int> &deadlines,
    const vector<Flight> &flights_desc,
    vector<int> &latest_out) {
  const int dcount = (int)deadlines.size();
  latest_out.assign(n * dcount * n, NEG_INF);

  for (int dest = 0; dest < n; dest++) {
    for (int dk = 0; dk < dcount; dk++) {
      int deadline = deadlines[dk];
      int base = latest_idx(n, dcount, dest, dk, 0);
      int *L = &latest_out[base];
      fill(L, L + n, NEG_INF);
      L[dest] = deadline;

      for (const auto &f : flights_desc) {
        if (f.t > deadline) {
          continue;
        }
        if (L[f.b] >= f.t && f.s > L[f.a]) {
          L[f.a] = f.s;
        }
      }
    }
  }
}

static long long evaluate_vci(
    int n,
    const vector<int> &deadlines,
    const vector<int> &sq_latest,
    const vector<int> &ci_latest,
    const vector<vector<pair<int, long long>>> &relevant_dests) {
  const int dcount = (int)deadlines.size();
  long long vci = 0;
  for (int orig = 0; orig < n; orig++) {
    for (const auto &[dest, wprod] : relevant_dests[orig]) {
      for (int dk = 0; dk < dcount; dk++) {
        int sq = sq_latest[latest_idx(n, dcount, dest, dk, orig)];
        int ci = ci_latest[latest_idx(n, dcount, dest, dk, orig)];
        if (sq < ci) {
          vci += wprod;
        }
      }
    }
  }
  return vci;
}

static long long estimate_delta_origin_only(
    int n,
    const vector<int> &deadlines,
    const vector<int> &sq_latest,
    const vector<int> &ci_latest,
    const vector<vector<pair<int, long long>>> &relevant_dests,
    int a,
    int b,
    int s,
    int t) {
  const int dcount = (int)deadlines.size();
  long long delta = 0;

  for (const auto &[dest, wprod] : relevant_dests[a]) {
    for (int dk = 0; dk < dcount; dk++) {
      int idx_b = latest_idx(n, dcount, dest, dk, b);
      if (ci_latest[idx_b] < t) {
        continue;
      }

      int idx_a = latest_idx(n, dcount, dest, dk, a);
      int old_ci = ci_latest[idx_a];
      if (old_ci >= s) {
        continue;
      }

      int sq = sq_latest[idx_a];
      bool old_win = (sq < old_ci);
      bool new_win = (sq < s);
      if (!old_win && new_win) {
        delta += wprod;
      }
    }
  }

  return delta;
}

static vector<pair<int, int>> collect_upstream_onehop(
    int n,
    const vector<City> &cities,
    const vector<Flight> &flights_desc,
    int a,
    int s,
    int max_upstream) {
  vector<int> best_dep(n, NEG_INF);
  best_dep[a] = s;
  for (const auto &f : flights_desc) {
    if (f.b == a && f.t <= s && f.s > best_dep[f.a]) {
      best_dep[f.a] = f.s;
    }
  }

  vector<int> ids;
  ids.reserve(n);
  for (int i = 0; i < n; i++) {
    if (best_dep[i] >= 0) {
      ids.push_back(i);
    }
  }
  sort(ids.begin(), ids.end(), [&](int p, int q) {
    if (best_dep[p] != best_dep[q]) return best_dep[p] > best_dep[q];
    if (cities[p].w != cities[q].w) return cities[p].w > cities[q].w;
    return p < q;
  });
  if ((int)ids.size() > max_upstream) {
    ids.resize(max_upstream);
  }

  vector<pair<int, int>> out;
  out.reserve(ids.size());
  for (int id : ids) {
    out.push_back({id, best_dep[id]});
  }
  return out;
}

static long long estimate_delta_upstream(
    int n,
    const vector<int> &deadlines,
    const vector<int> &sq_latest,
    const vector<int> &ci_latest,
    const vector<vector<pair<int, long long>>> &relevant_dests,
    const vector<pair<int, int>> &upstream_origins,
    int b,
    int t) {
  const int dcount = (int)deadlines.size();
  long long delta = 0;

  for (const auto &[orig, dep_tick] : upstream_origins) {
    for (const auto &[dest, wprod] : relevant_dests[orig]) {
      for (int dk = 0; dk < dcount; dk++) {
        int idx_b = latest_idx(n, dcount, dest, dk, b);
        if (ci_latest[idx_b] < t) {
          continue;
        }

        int idx_o = latest_idx(n, dcount, dest, dk, orig);
        int old_ci = ci_latest[idx_o];
        if (old_ci >= dep_tick) {
          continue;
        }

        int sq = sq_latest[idx_o];
        bool old_win = (sq < old_ci);
        bool new_win = (sq < dep_tick);
        if (!old_win && new_win) {
          delta += wprod;
        }
      }
    }
  }

  return delta;
}

static bool better_candidate(const Candidate &lhs, const Candidate &rhs) {
  if (!lhs.valid) return false;
  if (!rhs.valid) return true;
  if (lhs.delta != rhs.delta) return lhs.delta > rhs.delta;
  if (lhs.potential != rhs.potential) return lhs.potential > rhs.potential;
  if (lhs.pop != rhs.pop) return lhs.pop > rhs.pop;
  if (lhs.dur != rhs.dur) return lhs.dur < rhs.dur;
  return lhs.b < rhs.b;
}

static vector<Flight> build_legs_from_route(
    int start_city,
    int start_tick,
    const vector<int> &route,
    const vector<vector<int>> &dur) {
  vector<Flight> legs;
  int cur_city = start_city;
  int cur_t = start_tick;

  for (int dest : route) {
    if (dest < 0 || dest >= (int)dur.size()) {
      continue;
    }
    if (dest == cur_city) {
      continue;
    }
    int d = dur[cur_city][dest];
    if (d <= 0) {
      continue;
    }
    int arr = cur_t + d;
    if (arr > TICK_MAX) {
      break;
    }
    legs.push_back(Flight{cur_city, dest, cur_t, arr});
    cur_city = dest;
    cur_t = arr;
  }

  return legs;
}

static vector<int> route_from_legs(const vector<Flight> &legs) {
  vector<int> r;
  r.reserve(legs.size());
  for (const auto &f : legs) {
    r.push_back(f.b);
  }
  return r;
}

static vector<Flight> flatten_desc(const vector<vector<Flight>> &plane_legs) {
  vector<Flight> all;
  size_t total = 0;
  for (const auto &v : plane_legs) {
    total += v.size();
  }
  all.reserve(total);
  for (const auto &v : plane_legs) {
    for (const auto &f : v) {
      all.push_back(f);
    }
  }
  sort(all.begin(), all.end(), [](const Flight &p, const Flight &q) {
    if (p.s != q.s) return p.s > q.s;
    return p.t > q.t;
  });
  return all;
}

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

  auto solver_start = chrono::steady_clock::now();
  auto elapsed_sec = [&]() {
    auto now = chrono::steady_clock::now();
    return chrono::duration<double>(now - solver_start).count();
  };

  int n, r;
  cin >> n >> r;
  vector<City> cities(n);
  for (int i = 0; i < n; i++) {
    cin >> cities[i].x >> cities[i].y >> cities[i].w;
  }

  int m;
  cin >> m;
  vector<Flight> square(m);
  for (int i = 0; i < m; i++) {
    int a, b;
    string ss, tt;
    cin >> a >> ss >> b >> tt;
    --a;
    --b;
    square[i] = Flight{a, b, time_to_idx(ss), time_to_idx(tt)};
  }

  int k;
  cin >> k;

  vector<vector<int>> dur(n, vector<int>(n, 0));
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      if (i == j) continue;
      dur[i][j] = duration_ticks(cities[i], cities[j]);
    }
  }

  vector<int> deadlines;
  {
    int base = (11 * 60 - START_MIN) / STEP_MIN;
    for (int t = 0; t < 21; t++) {
      deadlines.push_back(base + t * 6);
    }
  }

  vector<Flight> square_desc = square;
  sort(square_desc.begin(), square_desc.end(), [](const Flight &p, const Flight &q) {
    if (p.s != q.s) return p.s > q.s;
    return p.t > q.t;
  });

  vector<int> sq_latest;
  compute_latest_all_sorted(n, deadlines, square_desc, sq_latest);

  vector<vector<pair<int, long long>>> relevant_dests(n);
  {
    long long rr = 1LL * r * r;
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        long long dx = 1LL * cities[i].x - cities[j].x;
        long long dy = 1LL * cities[i].y - cities[j].y;
        long long d2 = dx * dx + dy * dy;
        if (16LL * d2 >= rr) {
          relevant_dests[i].push_back({j, cities[i].w * cities[j].w});
        }
      }
    }
  }

  vector<vector<long long>> need_score(n, vector<long long>(TICK_MAX + 1, 0));
  {
    const int dcount = (int)deadlines.size();
    for (int orig = 0; orig < n; orig++) {
      for (int s = 0; s <= TICK_MAX; s++) {
        long long val = 0;
        for (const auto &[dest, wprod] : relevant_dests[orig]) {
          for (int dk = 0; dk < dcount; dk++) {
            int sq = sq_latest[latest_idx(n, dcount, dest, dk, orig)];
            if (sq < s) {
              val += wprod;
            }
          }
        }
        need_score[orig][s] = val;
      }
    }
  }

  vector<int> order(n);
  iota(order.begin(), order.end(), 0);
  sort(order.begin(), order.end(), [&](int a, int b) {
    return cities[a].w > cities[b].w;
  });

  vector<int> plane_start_city(k);
  vector<int> plane_start_tick(k, 0);
  vector<int> plane_city(k), plane_time(k, 0);
  vector<vector<Flight>> plane_legs(k);
  for (int p = 0; p < k; p++) {
    plane_start_city[p] = order[p % n];
    plane_city[p] = plane_start_city[p];
  }

  vector<Flight> circle_desc;
  vector<int> ci_latest;
  compute_latest_all_sorted(n, deadlines, circle_desc, ci_latest);

  auto insert_flight_desc = [&](const Flight &f) {
    auto it = lower_bound(
        circle_desc.begin(),
        circle_desc.end(),
        f,
        [](const Flight &x, const Flight &y) {
          if (x.s != y.s) return x.s > y.s;
          return x.t > y.t;
        });
    circle_desc.insert(it, f);
  };

  int steps = 0;
  while (true) {
    if (elapsed_sec() >= HP_TOTAL_LIMIT_SEC - HP_GREEDY_STOP_MARGIN_SEC) {
      break;
    }

    int p = -1;
    int best_time = TICK_MAX + 1;
    for (int i = 0; i < k; i++) {
      if (plane_time[i] <= TICK_MAX && plane_time[i] < best_time) {
        best_time = plane_time[i];
        p = i;
      }
    }
    if (p < 0) {
      break;
    }

    int a = plane_city[p];
    int s = plane_time[p];
    auto upstream_origins = collect_upstream_onehop(
        n, cities, circle_desc, a, s, HP_GREEDY_UPSTREAM_MAX);

    Candidate best;
    best.valid = false;

    for (int b = 0; b < n; b++) {
      if (b == a) continue;
      int d = dur[a][b];
      if (d <= 0) continue;
      int t = s + d;
      if (t > TICK_MAX) continue;

      long long delta = estimate_delta_upstream(
          n, deadlines, sq_latest, ci_latest, relevant_dests, upstream_origins, b, t);
      long long potential = need_score[b][t];

      Candidate cand;
      cand.valid = true;
      cand.b = b;
      cand.t = t;
      cand.dur = d;
      cand.delta = delta;
      cand.potential = potential;
      cand.pop = cities[b].w;

      if (better_candidate(cand, best)) {
        best = cand;
      }
    }

    if (!best.valid) {
      plane_time[p] = TICK_MAX + 1;
      continue;
    }

    if (best.delta <= 0 && best.potential <= 0) {
      plane_time[p] = TICK_MAX + 1;
      continue;
    }

    Flight f{a, best.b, s, best.t};
    plane_legs[p].push_back(f);
    insert_flight_desc(f);

    plane_city[p] = best.b;
    plane_time[p] = best.t;
    steps++;

    compute_latest_all_sorted(n, deadlines, circle_desc, ci_latest);

  }

  long long greedy_vci = evaluate_vci(n, deadlines, sq_latest, ci_latest, relevant_dests);

  // ---- Windowed local SA improvement ----
  vector<vector<int>> cur_routes(k);
  for (int p = 0; p < k; p++) {
    cur_routes[p] = route_from_legs(plane_legs[p]);
  }
  vector<int> cur_start_tick = plane_start_tick;
  vector<vector<Flight>> cur_legs = plane_legs;
  long long cur_score = greedy_vci;

  vector<vector<Flight>> best_legs = cur_legs;
  long long best_score = cur_score;

  auto evaluate_legs = [&](const vector<vector<Flight>> &legs) -> EvalResult {
    EvalResult e;
    e.desc = flatten_desc(legs);
    compute_latest_all_sorted(n, deadlines, e.desc, e.latest);
    e.score = evaluate_vci(n, deadlines, sq_latest, e.latest, relevant_dests);
    return e;
  };

  vector<Flight> proxy_desc = circle_desc;
  vector<int> proxy_latest = ci_latest;

  auto proxy_route_gain = [&](const vector<Flight> &legs) -> long long {
    long long val = 0;
    for (const auto &f : legs) {
      auto upstream = collect_upstream_onehop(
          n, cities, proxy_desc, f.a, f.s, HP_PROXY_UPSTREAM_MAX);
      long long g = estimate_delta_upstream(
          n, deadlines, sq_latest, proxy_latest, relevant_dests, upstream, f.b, f.t);
      g += need_score[f.b][f.t] / HP_PROXY_POTENTIAL_DIV;
      val += g;
    }
    return val;
  };

  vector<int> windows;
  for (int t = 0; t <= TICK_MAX; t += HP_WINDOW_STEP) {
    windows.push_back(t);
  }

  XorShift rng;
  double remain = HP_TOTAL_LIMIT_SEC - elapsed_sec();

  int sa_attempts = 0;
  int sa_accepts = 0;
  int sa_improves = 0;
  int sa_exact_evals = 0;
  int sa_prefilter_rejects = 0;
  int win_ptr = 0;

  if (remain > HP_SA_MIN_REMAIN_SEC) {
    double sa_start = elapsed_sec();
    double sa_limit = HP_TOTAL_LIMIT_SEC - HP_SA_STOP_MARGIN_SEC;
    const double LOG_T0 = log(HP_SA_T0);
    const double LOG_T1 = log(HP_SA_T1);

    while (elapsed_sec() < sa_limit) {
      sa_attempts++;

      int w0 = windows[win_ptr];
      win_ptr++;
      if (win_ptr >= (int)windows.size()) win_ptr = 0;
      int w1 = min(TICK_MAX + 1, w0 + HP_WINDOW_SIZE);

      int p = -1;
      vector<int> mutable_idx;
      for (int trial = 0; trial < k; trial++) {
        int cand_p = rng.next_int(0, k - 1);
        mutable_idx.clear();
        for (int idx = 0; idx < (int)cur_legs[cand_p].size(); idx++) {
          int s = cur_legs[cand_p][idx].s;
          if (w0 <= s && s < w1) {
            mutable_idx.push_back(idx);
          }
        }
        if (!mutable_idx.empty()) {
          p = cand_p;
          break;
        }
      }
      if (p < 0) {
        continue;
      }

      vector<int> cand_route = cur_routes[p];
      int cand_start = cur_start_tick[p];
      bool mutated = false;
      int move_type = rng.next_int(0, 99);

      auto pick_new_city = [&](int dep, int old) -> int {
        int L = min(n, HP_CITY_SAMPLE_TOP);
        for (int tr = 0; tr < HP_CITY_PICK_TRIES; tr++) {
          int b;
          if (rng.next_double() < HP_CITY_TOP_PICK_PROB) {
            b = order[rng.next_int(0, L - 1)];
          } else {
            b = rng.next_int(0, n - 1);
          }
          if (b != dep && b != old) return b;
        }
        return -1;
      };

      auto mutate_city_at = [&](int pos) {
        if (pos < 0 || pos >= (int)cand_route.size()) return;
        int dep = (pos == 0 ? plane_start_city[p] : cand_route[pos - 1]);
        int old = cand_route[pos];
        int nxt = pick_new_city(dep, old);
        if (nxt >= 0) {
          cand_route[pos] = nxt;
          mutated = true;
        }
      };

      if (move_type < HP_MOVE_CITY_CHANGE_PCT) {
        if (cand_route.empty() || mutable_idx.empty()) continue;
        int idx = mutable_idx[rng.next_int(0, (int)mutable_idx.size() - 1)];
        mutate_city_at(idx);
        if (rng.next_double() < HP_SECOND_CITY_MUTATE_PROB && !mutable_idx.empty()) {
          int idx2 = mutable_idx[rng.next_int(0, (int)mutable_idx.size() - 1)];
          mutate_city_at(idx2);
        }
      } else if (move_type < HP_MOVE_SHIFT_PCT) {
        if (cur_legs[p].empty()) continue;
        int d = HP_SHIFT_OPTIONS[rng.next_int(0, (int)HP_SHIFT_OPTIONS.size() - 1)];
        int ns = cand_start + d;
        ns = max(0, min(TICK_MAX, ns));
        if (ns != cand_start) {
          cand_start = ns;
          mutated = true;
        }
      } else if (move_type < HP_MOVE_INSERT_PCT) {
        if (mutable_idx.empty()) continue;
        int pos = mutable_idx[rng.next_int(0, (int)mutable_idx.size() - 1)];
        pos = max(0, min((int)cand_route.size(), pos));
        int dep = (pos == 0 ? plane_start_city[p] : cand_route[pos - 1]);
        int old = (pos < (int)cand_route.size() ? cand_route[pos] : -1);
        int nxt = pick_new_city(dep, old);
        if (nxt >= 0) {
          cand_route.insert(cand_route.begin() + pos, nxt);
          mutated = true;
        }
      } else {
        if (cand_route.empty() || mutable_idx.empty()) continue;
        int idx = mutable_idx[rng.next_int(0, (int)mutable_idx.size() - 1)];
        if (0 <= idx && idx < (int)cand_route.size()) {
          cand_route.erase(cand_route.begin() + idx);
          mutated = true;
        }
      }

      if (!mutated) {
        continue;
      }

      vector<Flight> cand_legs_p =
          build_legs_from_route(plane_start_city[p], cand_start, cand_route, dur);
      cand_route = route_from_legs(cand_legs_p);

      // ---- diff-style prefilter (cheap proxy delta) ----
      long long old_proxy = proxy_route_gain(cur_legs[p]);
      long long new_proxy = proxy_route_gain(cand_legs_p);
      long long proxy_delta = new_proxy - old_proxy;

      double progress = (elapsed_sec() - sa_start) / max(1e-9, (sa_limit - sa_start));
      progress = min(1.0, max(0.0, progress));
      double temp = exp(LOG_T0 * (1.0 - progress) + LOG_T1 * progress);

      if (proxy_delta < 0) {
        double pre_x = (double)proxy_delta / (temp * HP_PREFILTER_TEMP_SCALE);
        if (pre_x <= HP_EXP_FLOOR || rng.next_double() >= exp(pre_x)) {
          sa_prefilter_rejects++;
          continue;
        }
      }

      vector<vector<Flight>> nxt_legs = cur_legs;
      nxt_legs[p] = std::move(cand_legs_p);
      sa_exact_evals++;

      EvalResult nxt_eval = evaluate_legs(nxt_legs);
      long long delta = nxt_eval.score - cur_score;

      bool accept = false;
      if (delta >= 0) {
        accept = true;
      } else {
        double x = (double)delta / temp;
        if (x > HP_EXP_FLOOR && rng.next_double() < exp(x)) {
          accept = true;
        }
      }

      if (!accept) {
        continue;
      }

      sa_accepts++;
      cur_score = nxt_eval.score;
      cur_legs = std::move(nxt_legs);
      cur_routes[p] = std::move(cand_route);
      cur_start_tick[p] = cand_start;
      proxy_desc = std::move(nxt_eval.desc);
      proxy_latest = std::move(nxt_eval.latest);

      if (cur_score > best_score) {
        best_score = cur_score;
        best_legs = cur_legs;
        sa_improves++;
      }
    }
  }

  cerr << "[summary] greedy_v_ci=" << greedy_vci
       << " best_v_ci=" << best_score
       << " sa_attempts=" << sa_attempts
       << " sa_exact_evals=" << sa_exact_evals
       << " sa_prefilter_rejects=" << sa_prefilter_rejects
       << " sa_accepts=" << sa_accepts
       << " sa_improves=" << sa_improves
       << " elapsed=" << elapsed_sec() << "s\n";

  for (int p = 0; p < k; p++) {
    cout << best_legs[p].size() << '\n';
    for (const auto &f : best_legs[p]) {
      cout << (f.a + 1) << ' ' << idx_to_time(f.s) << ' '
           << (f.b + 1) << ' ' << idx_to_time(f.t) << '\n';
    }
  }

  return 0;
}
0