結果

問題 No.5023 Airlines Optimization
コンテスト
ユーザー Piiiii
提出日時 2026-03-04 20:11:30
言語 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
結果
TLE  
実行時間 -
コード長 27,183 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 7,354 ms
コンパイル使用メモリ 427,760 KB
実行使用メモリ 42,996 KB
スコア 25,376,511
最終ジャッジ日時 2026-03-04 22:11:00
合計ジャッジ時間 121,263 ms
ジャッジサーバーID
(参考情報)
judge3 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 40 TLE * 60
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#pragma GCC optimize("O3,unroll-loops")
#include <bits/stdc++.h>
using namespace std;

static constexpr int MAXN = 47;
static constexpr int SLOT_MIN = 5;
static constexpr int DAY_START_MIN = 6 * 60;   // 06:00
static constexpr int DAY_END_MIN   = 21 * 60;  // 21:00
static constexpr int TS = (DAY_END_MIN - DAY_START_MIN) / SLOT_MIN + 1; // 181
static constexpr int V  = MAXN * TS;

// ===== Fixed hyperparameters =====
static constexpr int ORIGIN_USE = 47;
static constexpr int START_USE  = 47;
static constexpr int NEAR_CAND  = 18;
static constexpr int POP_CAND   = 26;

static constexpr int K_FIXED = 25;
static constexpr int M_FIXED = 400;
static constexpr int BEAM_WIDTH_DEFAULT = 3;
static constexpr int BRANCHES_DEFAULT = 2;
static int gBeamWidth = BEAM_WIDTH_DEFAULT;
static int gBranches = BRANCHES_DEFAULT;

// Tunable parameters for tie-breaking / branch scoring.
static int gTieMove = 0;
static int gTiePopDiv = 0;
static int gTieArr = 1;
static int gBranchMode = 0; // 0: default, 1: spatial-diverse, 2: time-diverse
static int gBranchDivW = 0;
static int gScoreMode = 1; // 0: cumulative dp, 1: parent true + dp
static long long gBeamGap = 100; // keep states with score >= best - gap
static int gVerboseStep = 0;
static long long gEvalFlightW = 0;
static long long gEvalPopDivW = 1;
static long long gEvalLateArrW = 0;

// Targets: 11:00, 11:30, ..., 21:00 (21 values)
static constexpr int TGT = 21;

struct FlightOut {
  int a, b;
  int dep_t, arr_t; // time index
};

struct RouteCandidate {
  vector<FlightOut> flights;
  __int128 value;
};

static inline int timeToIdx(int minute) { return (minute - DAY_START_MIN) / SLOT_MIN; }
static inline int idxToTime(int idx) { return DAY_START_MIN + idx * SLOT_MIN; }
static inline string fmtTimeIdx(int idx) {
  int m = idxToTime(idx);
  int hh = m / 60, mm = m % 60;
  char buf[6];
  snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm);
  return string(buf);
}
static inline int parseTimeStr(const string& s) {
  int hh = (s[0]-'0')*10 + (s[1]-'0');
  int mm = (s[3]-'0')*10 + (s[4]-'0');
  return hh*60 + mm;
}

static inline bool parseEnvInt(const char* name, int& out) {
  const char* s = getenv(name);
  if (!s) return false;
  out = atoi(s);
  return true;
}

// ===== globals =====
static uint32_t popW[MAXN];
static int N, R;

static int durSlot[MAXN][MAXN];
static double distMat[MAXN][MAXN];
static bool validPair[MAXN][MAXN];

// earliest[d][t][c] (time-major for cache)
static uint16_t earliestSq[MAXN][TS][MAXN];
static uint16_t earliestCi[MAXN][TS][MAXN];
static constexpr uint16_t INF16 = 65535;

// latest[o][d][deadline] (same as before)
static int16_t latestSq[MAXN][MAXN][TS];
static int16_t latestCi[MAXN][MAXN][TS];

static int16_t s_sq[MAXN][MAXN][TGT];
static int16_t s_ci[MAXN][MAXN][TGT];

// reachMask[vtx][ti] : destinations reachable by target ti from vtx
static uint64_t reachMask[V][TGT];

// flipMask[oi][s][ti]  (IMPORTANT: ti contiguous now)
static uint64_t flipMask[ORIGIN_USE][TS][TGT];

static int tgtIdx[TGT];
static int firstTiTime[TS]; // first target index whose deadline >= timeIdx (or TGT)

// Precomputed feasible flight transitions from each vertex (c,t).
static uint8_t dpMoveCnt[V];
static uint16_t dpMoveV[V][MAXN];
static uint8_t dpMoveStartTi[V][MAXN];

static inline long long routeEvalBonus(const vector<FlightOut>& flights) {
  long long bonus = 0;
  bonus += gEvalFlightW * (long long)flights.size();
  if (!flights.empty()) {
    long long popDiv = 0;
    int lastArr = flights.back().arr_t;
    bool seen[MAXN];
    memset(seen, 0, sizeof(seen));
    for (const auto& f : flights) {
      if (!seen[f.b]) {
        seen[f.b] = true;
        popDiv += popW[f.b] / 1000;
      }
    }
    bonus += gEvalPopDivW * popDiv;
    bonus += gEvalLateArrW * (long long)lastArr;
  }
  return bonus;
}

// ===== sumW(mask) tables =====
static uint32_t T0[1<<16];
static uint32_t T1[1<<16];
static uint32_t T2[1<<15];

static inline void buildWeightTables(const uint32_t* w) {
  T0[0]=0;
  for (uint32_t m=1; m<(1u<<16); m++) {
    uint32_t b = __builtin_ctz(m);
    T0[m] = T0[m & (m-1)] + w[b];
  }
  T1[0]=0;
  for (uint32_t m=1; m<(1u<<16); m++) {
    uint32_t b = __builtin_ctz(m);
    T1[m] = T1[m & (m-1)] + w[16 + b];
  }
  T2[0]=0;
  for (uint32_t m=1; m<(1u<<15); m++) {
    uint32_t b = __builtin_ctz(m);
    T2[m] = T2[m & (m-1)] + w[32 + b];
  }
}
static inline uint64_t sumW_mask(uint64_t mask) {
  uint32_t a = (uint32_t)(mask & 0xFFFFull);
  uint32_t b = (uint32_t)((mask >> 16) & 0xFFFFull);
  uint32_t c = (uint32_t)((mask >> 32) & 0x7FFFull);
  return T0[a] + T1[b] + T2[c];
}

// ===== CSR adjacency (contiguous edges per u) =====
template<int MAXE>
struct CSRAdj {
  int E = 0;
  int u[MAXE];
  uint8_t toCity[MAXE];
  uint8_t arrT[MAXE];

  int start[V+1];        // size V+1
  uint8_t bTo[MAXE];
  uint8_t bArr[MAXE];

  void init() { E = 0; }

  inline void addEdge(int uV, int vCity, int at) {
    u[E] = uV;
    toCity[E] = (uint8_t)vCity;
    arrT[E] = (uint8_t)at;
    ++E;
  }

  void build() {
    static int cnt[V];
    static int cur[V];
    memset(cnt, 0, sizeof(cnt));
    for (int i=0;i<E;i++) cnt[u[i]]++;

    start[0] = 0;
    for (int i=0;i<V;i++) start[i+1] = start[i] + cnt[i];

    memcpy(cur, start, sizeof(int)*V);
    for (int i=0;i<E;i++) {
      int pos = cur[u[i]]++;
      bTo[pos]  = toCity[i];
      bArr[pos] = arrT[i];
    }
  }
};

static CSRAdj<M_FIXED + 8> sqAdj;
static CSRAdj<20000> ciAdj;

// ===== build earliest + latest =====
template<int MAXE>
static inline void buildEarliestTable(const CSRAdj<MAXE>& adj,
                                      uint16_t earliest[MAXN][TS][MAXN]) {
  // earliest[d][t][c] = min arrival time at d starting from (c,t)
  for (int d=0; d<N; d++) {
    for (int t=TS-1; t>=0; --t) {
      uint16_t* row = earliest[d][t];
      uint16_t* rowWait = (t+1 < TS) ? earliest[d][t+1] : nullptr;

      for (int c=0; c<N; c++) {
        uint16_t best = INF16;
        if (c == d) best = (uint16_t)t;
        if (rowWait) best = min(best, rowWait[c]); // wait

        int uV = c*TS + t;
        for (int ei = adj.start[uV]; ei < adj.start[uV+1]; ++ei) {
          best = min(best, earliest[d][adj.bArr[ei]][adj.bTo[ei]]);
        }
        row[c] = best;
      }
    }
  }
}

static inline void buildLatestTable(const uint16_t earliest[MAXN][TS][MAXN],
                                    int16_t latest[MAXN][MAXN][TS]) {
  // latest[o][d][deadline] from earliest
  static int16_t bucket[TS];
  for (int o=0; o<N; o++) {
    for (int d=0; d<N; d++) {
      for (int i=0;i<TS;i++) bucket[i] = -1;
      for (int s=0; s<TS; s++) {
        uint16_t arr = earliest[d][s][o];
        if (arr != INF16) bucket[arr] = max(bucket[arr], (int16_t)s);
      }
      int16_t best = -1;
      for (int dead=0; dead<TS; dead++) {
        best = max(best, bucket[dead]);
        latest[o][d][dead] = best;
      }
    }
  }
}

template<int MAXE>
static inline void buildEarliestAndLatest(const CSRAdj<MAXE>& adj,
                                          uint16_t earliest[MAXN][TS][MAXN],
                                          int16_t latest[MAXN][MAXN][TS]) {
  buildEarliestTable(adj, earliest);
  buildLatestTable(earliest, latest);
}

static void buildTargetsFromLatest(const int16_t latest[MAXN][MAXN][TS],
                                  int16_t out[MAXN][MAXN][TGT]) {
  for (int o=0; o<N; o++)
    for (int d=0; d<N; d++)
      for (int ti=0; ti<TGT; ti++)
        out[o][d][ti] = latest[o][d][tgtIdx[ti]];
}

// ===== reachMask: O(V*(N+TGT)) =====
static void buildReachMaskFromEarliestFast(const uint16_t earliest[MAXN][TS][MAXN]) {
  uint64_t tmp[TGT];
  for (int vtx=0; vtx<N*TS; vtx++) {
    int c = vtx / TS;
    int t = vtx % TS;
    for (int i=0;i<TGT;i++) tmp[i]=0;

    for (int d=0; d<N; d++) {
      uint16_t arr = earliest[d][t][c];
      if (arr == INF16) continue;
      int k = firstTiTime[(int)arr];
      if (k < TGT) tmp[k] |= (1ull << d);
    }

    uint64_t cur=0;
    for (int ti=0; ti<TGT; ti++) {
      cur |= tmp[ti];
      reachMask[vtx][ti] = cur;
    }
  }
}

// build flipMask[oi][s][ti]
static void buildFlipMaskForOrigins(const int originsUsed[ORIGIN_USE], int originsCount) {
  for (int oi=0; oi<originsCount; oi++) {
    int o = originsUsed[oi];
    for (int ti=0; ti<TGT; ti++) {
      uint64_t addAt[TS];
      memset(addAt, 0, sizeof(addAt));
      for (int d=0; d<N; d++) {
        if (!validPair[o][d]) continue;
        if (s_ci[o][d][ti] > s_sq[o][d][ti]) continue;
        int16_t sq = s_sq[o][d][ti];
        int thr = (sq < 0 ? 0 : (int)sq + 1);
        if (thr <= TS-1) addAt[thr] |= (1ull << d);
      }
      uint64_t cur=0;
      for (int s=0; s<TS; s++) {
        cur |= addAt[s];
        flipMask[oi][s][ti] = cur;
      }
    }
  }
}

// ===== active origins per vertex (cache fp pointer) =====
struct ActOrigins {
  uint8_t cnt;
  uint64_t wo[ORIGIN_USE];
  const uint64_t* fp0[ORIGIN_USE]; // points to flipMask[oi][sidx][0]
};
static ActOrigins act[V];

static void buildActOrigins(const int16_t (*originLatest[ORIGIN_USE])[TS],
                            const uint64_t originW[ORIGIN_USE],
                            int originsCount) {
  for (int u=0; u<N*TS; u++) {
    int c = u/TS;
    int t = u%TS;
    uint8_t cnt=0;
    for (int oi=0; oi<originsCount; oi++) {
      int16_t s = originLatest[oi][c][t];
      if (s < 0) continue;
      act[u].wo[cnt]  = originW[oi];
      act[u].fp0[cnt] = &flipMask[oi][(int)s][0];
      cnt++;
    }
    act[u].cnt = cnt;
  }
}

// ===== edgeGain (faster due to flipMask layout) =====
static inline uint64_t edgeGainUV(int u, int vtx, int startTi) {
  uint64_t gain = 0;
  const ActOrigins& A = act[u];
  const uint64_t* rm = reachMask[vtx];
  uint64_t lastMask = ~0ull;
  uint64_t lastSum = 0;

  for (int k=0; k<(int)A.cnt; k++) {
    uint64_t wo = A.wo[k];
    const uint64_t* fp = A.fp0[k]; // fp[ti] is flipMask for this origin and sidx

    for (int ti=startTi; ti<TGT; ti++) {
      uint64_t m = fp[ti] & rm[ti];
      if (!m) continue;
      uint64_t sw;
      if (m == lastMask) sw = lastSum;
      else {
        sw = sumW_mask(m);
        lastMask = m;
        lastSum = sw;
      }
      gain += wo * sw;
    }
  }
  return gain;
}

static vector<FlightOut> restoreFlightsFromEnd(int endVtx, const int prevv[V]) {
  vector<FlightOut> revFlights;
  int cur = endVtx;
  while (cur != -1) {
    int p = prevv[cur];
    if (p == -1) break;
    int pc = p / TS, pt = p % TS;
    int cc = cur / TS, ct = cur % TS;
    if (pc != cc) revFlights.push_back(FlightOut{pc, cc, pt, ct});
    cur = p;
  }
  reverse(revFlights.begin(), revFlights.end());
  return revFlights;
}

// ===== top-2 DP routes for one plane (different end cities) =====
static vector<RouteCandidate> solveOnePlaneDPTopK(const vector<int>& startCities) {
  static __int128 dp[V];
  static int prevv[V];
  static long long dpSec[V];
  static int16_t dpMove[V];
  static int32_t dpPopDiv[V];

  const __int128 NEG = -((__int128)1 << 120);
  const long long NEG_SEC = numeric_limits<long long>::min() / 4;
  for (int i = 0; i < V; i++) {
    dp[i] = NEG;
    prevv[i] = -1;
    dpSec[i] = NEG_SEC;
    dpMove[i] = -1;
    dpPopDiv[i] = -1;
  }

  for (int c : startCities) {
    dp[c * TS] = 0;
    dpSec[c * TS] = 0;
    dpMove[c * TS] = 0;
    dpPopDiv[c * TS] = 0;
  }

  auto betterState = [&](int v, __int128 nv, long long ns, int16_t nm, int32_t np, int p) {
    if (nv != dp[v]) return nv > dp[v];
    if (ns != dpSec[v]) return ns > dpSec[v];
    if (nm != dpMove[v]) return nm > dpMove[v];
    if (np != dpPopDiv[v]) return np > dpPopDiv[v];
    return (prevv[v] < 0 || p < prevv[v]);
  };

  for (int t = 0; t < TS; t++) {
    for (int c = 0; c < N; c++) {
      int u = c * TS + t;
      __int128 cur = dp[u];
      if (cur == NEG) continue;

      // wait
      if (t + 1 < TS) {
        int v = u + 1;
        if (betterState(v, cur, dpSec[u], dpMove[u], dpPopDiv[u], u)) {
          dp[v] = cur;
          dpSec[v] = dpSec[u];
          dpMove[v] = dpMove[u];
          dpPopDiv[v] = dpPopDiv[u];
          prevv[v] = u;
        }
      }

      // moves
      int mcnt = (int)dpMoveCnt[u];
      const uint16_t* mv = dpMoveV[u];
      const uint8_t* ms = dpMoveStartTi[u];
      for (int mi = 0; mi < mcnt; mi++) {
        int v = (int)mv[mi];
        int st = (int)ms[mi];
        uint64_t g = edgeGainUV(u, v, st);
        __int128 nv = cur + (__int128)g;
        int toCity = v / TS;
        int at = v % TS;
        int popDiv = (int)(popW[toCity] / 1000);
        long long stepSec = (long long)gTieMove
                          + 1LL * gTiePopDiv * popDiv
                          + 1LL * gTieArr * at;
        long long ns = dpSec[u] + stepSec;
        int16_t nm = (int16_t)(dpMove[u] + 1);
        int32_t np = dpPopDiv[u] + popDiv;
        if (betterState(v, nv, ns, nm, np, u)) {
          dp[v] = nv;
          dpSec[v] = ns;
          dpMove[v] = nm;
          dpPopDiv[v] = np;
          prevv[v] = u;
        }
      }
    }
  }

  vector<RouteCandidate> out;
  out.reserve(gBranches);

  static __int128 cityBestVal[MAXN];
  static int cityBestVtx[MAXN];
  static long long cityBestSec[MAXN];
  static int16_t cityBestMove[MAXN];
  static int32_t cityBestPopDiv[MAXN];
  for (int city = 0; city < N; city++) {
    cityBestVal[city] = NEG;
    cityBestVtx[city] = -1;
    cityBestSec[city] = NEG_SEC;
    cityBestMove[city] = -1;
    cityBestPopDiv[city] = -1;
  }
  for (int v = 0; v < V; v++) {
    if (dp[v] == NEG) continue;
    int city = v / TS;
    if (dp[v] > cityBestVal[city] ||
        (dp[v] == cityBestVal[city] && (
          dpSec[v] > cityBestSec[city] ||
          (dpSec[v] == cityBestSec[city] && (
            dpMove[v] > cityBestMove[city] ||
            (dpMove[v] == cityBestMove[city] && (
              dpPopDiv[v] > cityBestPopDiv[city] ||
              (dpPopDiv[v] == cityBestPopDiv[city] &&
               (cityBestVtx[city] < 0 || v < cityBestVtx[city]))
            ))
          ))
        ))) {
      cityBestVal[city] = dp[v];
      cityBestVtx[city] = v;
      cityBestSec[city] = dpSec[v];
      cityBestMove[city] = dpMove[v];
      cityBestPopDiv[city] = dpPopDiv[v];
    }
  }

  bool usedEndCity[MAXN];
  memset(usedEndCity, 0, sizeof(usedEndCity));
  int selectedCities[MAXN];
  int selectedArr[MAXN];
  int selectedCnt = 0;
  for (int k = 0; k < gBranches; k++) {
    __int128 bestVal = NEG;
    int bestCity = -1;
    int bestVtx = -1;
    long long bestSec = NEG_SEC;
    int16_t bestMove = -1;
    int32_t bestPopDiv = -1;
    for (int city = 0; city < N; city++) {
      if (usedEndCity[city]) continue;
      int v = cityBestVtx[city];
      if (v < 0) continue;
      __int128 val = cityBestVal[city];
      long long sec = cityBestSec[city];
      if (gBranchMode == 1 && selectedCnt > 0) {
        double minD = 1e100;
        for (int i = 0; i < selectedCnt; i++) minD = min(minD, distMat[city][selectedCities[i]]);
        sec += (long long)llround(minD * gBranchDivW);
      } else if (gBranchMode == 2 && selectedCnt > 0) {
        int arr = v % TS;
        int minDt = TS;
        for (int i = 0; i < selectedCnt; i++) minDt = min(minDt, abs(arr - selectedArr[i]));
        sec += 1LL * gBranchDivW * minDt;
      }
      if (val > bestVal ||
          (val == bestVal && (sec > bestSec ||
            (sec == bestSec && (cityBestMove[city] > bestMove ||
              (cityBestMove[city] == bestMove && (cityBestPopDiv[city] > bestPopDiv ||
                (cityBestPopDiv[city] == bestPopDiv &&
                 (bestVtx < 0 || v < bestVtx))))))))) {
        bestVal = val;
        bestCity = city;
        bestVtx = v;
        bestSec = sec;
        bestMove = cityBestMove[city];
        bestPopDiv = cityBestPopDiv[city];
      }
    }
    if (bestCity < 0) break;
    usedEndCity[bestCity] = true;
    selectedCities[selectedCnt] = bestCity;
    selectedArr[selectedCnt] = bestVtx % TS;
    selectedCnt++;
    out.push_back(RouteCandidate{restoreFlightsFromEnd(bestVtx, prevv), bestVal});
  }

  return out;
}

static vector<vector<int>> buildCandidateDestinations(const vector<int>& popOrder) {
  // nearest + popular candidates, then deduplicated in ascending city index
  vector<vector<int>> candDest(N);
  for (int i=0;i<N;i++){
    vector<pair<double,int>> near;
    near.reserve(N-1);
    for (int j=0;j<N;j++){
      if (i==j) continue;
      near.push_back({distMat[i][j], j});
    }
    sort(near.begin(), near.end());

    vector<int> cand;
    cand.reserve(min(NEAR_CAND, N-1) + min(POP_CAND, N-1) + 4);

    int takeNear = min(NEAR_CAND, N-1);
    for (int k=0; k<(int)near.size() && (int)cand.size()<takeNear; k++){
      cand.push_back(near[k].second);
    }

    int takePop = min(POP_CAND, N-1);
    int addedPop = 0;
    for (int k=0; k<N && addedPop<takePop; k++){
      int j = popOrder[k];
      if (j==i) continue;
      cand.push_back(j);
      addedPop++;
    }

    sort(cand.begin(), cand.end());
    cand.erase(unique(cand.begin(), cand.end()), cand.end());
    candDest[i]=cand;
  }
  return candDest;
}

static vector<int> buildStartCities(const vector<int>& popOrder) {
  vector<int> startCities;
  for (int i=0;i<min(START_USE, N); i++) {
    startCities.push_back(popOrder[i]);
  }
  return startCities;
}

static void buildDpTransitions(const vector<vector<int>>& candDest) {
  // Keep candidate order to preserve exact DP tie behavior.
  for (int c=0; c<N; c++) {
    const auto& cd = candDest[c];
    for (int t=0; t<TS; t++) {
      int u = c*TS + t;
      uint8_t cnt = 0;
      for (int j: cd) {
        int at = t + durSlot[c][j];
        if (at >= TS) continue;
        dpMoveV[u][cnt] = (uint16_t)(j*TS + at);
        dpMoveStartTi[u][cnt] = (uint8_t)firstTiTime[at];
        cnt++;
      }
      dpMoveCnt[u] = cnt;
    }
  }
}

static int buildOrigins(const vector<int>& popOrder,
                        int originsUsed[ORIGIN_USE],
                        uint64_t originW[ORIGIN_USE],
                        const int16_t (*originLatest[ORIGIN_USE])[TS]) {
  int originsCount = 0;
  for (int i=0; i<min(ORIGIN_USE, N); i++) {
    int city = popOrder[i];
    originsUsed[originsCount] = city;
    originW[originsCount] = (uint64_t)popW[city];
    originLatest[originsCount] = latestCi[city];
    originsCount++;
  }
  return originsCount;
}

// ===== debug score =====
static long long evalScoreEstimate() {
  __int128 vci=0, vsq=0;
  for (int o=0;o<N;o++){
    for (int d=0;d<N;d++){
      if (!validPair[o][d]) continue;
      __int128 ww = (__int128)popW[o] * (__int128)popW[d];
      for (int ti=0;ti<TGT;ti++){
        if (s_ci[o][d][ti] > s_sq[o][d][ti]) vci += ww;
        else vsq += ww;
      }
    }
  }
  if (vci+vsq==0) return 0;
  long double S = (long double)vci / (long double)(vci+vsq);
  return (long long)floor(1e6L * S + 1e-12L);
}

struct BeamState {
  CSRAdj<20000> ci;
  vector<vector<FlightOut>> routes;
  __int128 score;
};

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

  // Skip UTF-8 BOM if present in local test files.
  if (cin.peek() == 0xEF) {
    char b1 = 0, b2 = 0, b3 = 0;
    cin.get(b1);
    cin.get(b2);
    cin.get(b3);
    if (!(b1 == (char)0xEF && b2 == (char)0xBB && b3 == (char)0xBF)) {
      cin.clear();
    }
  }

  {
    int v;
    if (parseEnvInt("SC_BEAM_WIDTH", v) && v >= 1) gBeamWidth = v;
    if (parseEnvInt("SC_BRANCHES", v) && v >= 1) gBranches = v;
    if (parseEnvInt("SC_TIE_MOVE", v)) gTieMove = v;
    if (parseEnvInt("SC_TIE_POPDIV", v)) gTiePopDiv = v;
    if (parseEnvInt("SC_TIE_ARR", v)) gTieArr = v;
    if (parseEnvInt("SC_BRANCH_MODE", v)) gBranchMode = v;
    if (parseEnvInt("SC_BRANCH_DIVW", v)) gBranchDivW = v;
    if (parseEnvInt("SC_SCORE_MODE", v)) gScoreMode = v;
    if (parseEnvInt("SC_BEAM_GAP", v)) gBeamGap = v;
    if (parseEnvInt("SC_VERBOSE_STEP", v)) gVerboseStep = v;
    if (parseEnvInt("SC_EVAL_FLIGHT_W", v)) gEvalFlightW = v;
    if (parseEnvInt("SC_EVAL_POPDIV_W", v)) gEvalPopDivW = v;
    if (parseEnvInt("SC_EVAL_LATEARR_W", v)) gEvalLateArrW = v;
  }

  cin >> N >> R;
  vector<int> xs(N), ys(N);
  for (int i=0;i<N;i++) cin >> xs[i] >> ys[i] >> popW[i];

  for (int i=0;i<TGT;i++){
    int minute = 11*60 + 30*i;
    tgtIdx[i] = timeToIdx(minute);
  }
  {
    int cur=0;
    for (int t=0;t<TS;t++){
      while (cur<TGT && tgtIdx[cur] < t) cur++;
      firstTiTime[t] = cur;
    }
  }

  double thr = 0.25 * (double)R;
  for (int i=0;i<N;i++){
    for (int j=0;j<N;j++){
      double dx = (double)xs[i]-xs[j];
      double dy = (double)ys[i]-ys[j];
      double d = sqrt(dx*dx + dy*dy);
      distMat[i][j]=d;
      validPair[i][j] = (d >= thr);
      if (i==j) durSlot[i][j]=0;
      else {
        double raw = 60.0 * d / 800.0 + 40.0;
        int q = (int)ceil(raw / 5.0 - 1e-12);
        durSlot[i][j] = q; // in 5-min slots
      }
    }
  }

  buildWeightTables(popW);

  int M;
  cin >> M;
  sqAdj.init();
  for (int k=0;k<M;k++){
    int a,b;
    string ss, tt;
    cin >> a >> ss >> b >> tt;
    --a; --b;
    int s = timeToIdx(parseTimeStr(ss));
    int t = timeToIdx(parseTimeStr(tt));
    sqAdj.addEdge(a*TS + s, b, t);
  }
  sqAdj.build();

  int K;
  cin >> K;
  (void)K;

  ciAdj.init();

  // Square: earliest/latest/targets
  buildEarliestAndLatest(sqAdj, earliestSq, latestSq);
  buildTargetsFromLatest(latestSq, s_sq);

  // population order
  vector<int> popOrder(N);
  iota(popOrder.begin(), popOrder.end(), 0);
  sort(popOrder.begin(), popOrder.end(), [&](int a,int b){ return popW[a]>popW[b]; });

  vector<vector<int>> candDest = buildCandidateDestinations(popOrder);
  vector<int> startCities = buildStartCities(popOrder);
  buildDpTransitions(candDest);

  // origins: all cities (pop order)
  int originsUsed[ORIGIN_USE];
  uint64_t originW[ORIGIN_USE];
  const int16_t (*originLatest[ORIGIN_USE])[TS];
  int originsCount = buildOrigins(popOrder, originsUsed, originW, originLatest);

  vector<BeamState> beam;
  beam.reserve(gBeamWidth);
  BeamState root;
  root.ci.init();
  root.routes.reserve(K_FIXED);
  root.score = 0;
  beam.push_back(move(root));

  for (int plane = 0; plane < K_FIXED; plane++) {
    vector<BeamState> nextBeam;
    nextBeam.reserve(gBeamWidth * gBranches);

    for (auto& st : beam) {
      st.ci.build();

      buildEarliestAndLatest(st.ci, earliestCi, latestCi);
      buildTargetsFromLatest(latestCi, s_ci);
      long long parentTrueScore = 0;
      if (gScoreMode == 1) parentTrueScore = evalScoreEstimate();
      buildReachMaskFromEarliestFast(earliestCi);
      buildFlipMaskForOrigins(originsUsed, originsCount);
      buildActOrigins(originLatest, originW, originsCount);

      auto candRoutes = solveOnePlaneDPTopK(startCities);
      if (candRoutes.empty()) candRoutes.push_back(RouteCandidate{{}, 0});

      for (int i = 0; i < (int)candRoutes.size(); i++) {
        BeamState child = st;
        if (gScoreMode == 1) {
          child.score = (__int128)parentTrueScore + candRoutes[i].value;
        } else {
          child.score = st.score + candRoutes[i].value;
        }
        child.score += (__int128)routeEvalBonus(candRoutes[i].flights);
        child.routes.push_back(candRoutes[i].flights);
        for (const auto& f : candRoutes[i].flights) {
          int uV = f.a * TS + f.dep_t;
          child.ci.addEdge(uV, f.b, f.arr_t);
        }
        nextBeam.push_back(move(child));
      }
    }

    if (nextBeam.empty()) {
      cerr << "[beam step " << (plane + 1) << "] no candidates\n";
      break;
    }

    sort(nextBeam.begin(), nextBeam.end(), [](const BeamState& a, const BeamState& b) {
      return a.score > b.score;
    });
    if ((int)nextBeam.size() > gBeamWidth) nextBeam.resize(gBeamWidth);
    if (gBeamGap >= 0 && !nextBeam.empty()) {
      __int128 thr = nextBeam[0].score - (__int128)gBeamGap;
      int keep = 0;
      while (keep < (int)nextBeam.size() && nextBeam[keep].score >= thr) keep++;
      if (keep <= 0) keep = 1;
      if (keep < (int)nextBeam.size()) nextBeam.resize(keep);
    }
    beam.swap(nextBeam);

    if (gVerboseStep) {
      cerr << "[beam step " << (plane + 1) << "] beam_size=" << beam.size()
           << " best_flights=" << beam[0].routes.back().size() << "\n";
    }
  }

  BeamState best = beam[0];
  vector<vector<FlightOut>> routes = best.routes;
  if ((int)routes.size() < K_FIXED) routes.resize(K_FIXED);
  auto buildCiFromRoutes = [&](const vector<vector<FlightOut>>& rs, CSRAdj<20000>& outCi) {
    outCi.init();
    for (const auto& r : rs) {
      for (const auto& f : r) outCi.addEdge(f.a * TS + f.dep_t, f.b, f.arr_t);
    }
  };
  auto evalCiScore = [&](CSRAdj<20000>& ci) -> long long {
    ci.build();
    buildEarliestAndLatest(ci, earliestCi, latestCi);
    buildTargetsFromLatest(latestCi, s_ci);
    return evalScoreEstimate();
  };
  auto sameRoute = [&](const vector<FlightOut>& a, const vector<FlightOut>& b) -> bool {
    if (a.size() != b.size()) return false;
    for (int i = 0; i < (int)a.size(); i++) {
      if (a[i].a != b[i].a || a[i].b != b[i].b || a[i].dep_t != b[i].dep_t || a[i].arr_t != b[i].arr_t) return false;
    }
    return true;
  };

  buildCiFromRoutes(routes, ciAdj);
  long long bestTrueScore = evalCiScore(ciAdj);

  // Small coordinate improvement: re-optimize weak (short) routes a few times.
  {
    static constexpr int REFINE_TRIALS = 3;
    bool usedPlane[K_FIXED];
    memset(usedPlane, 0, sizeof(usedPlane));
    for (int tr = 0; tr < REFINE_TRIALS; tr++) {
      int targetPlane = -1;
      long long worstBonus = (1LL << 60);
      int bestLen = INT_MAX;
      for (int p = 0; p < K_FIXED; p++) {
        if (usedPlane[p]) continue;
        long long b = routeEvalBonus(routes[p]);
        int len = (int)routes[p].size();
        if (b < worstBonus || (b == worstBonus && len < bestLen)) {
          worstBonus = b;
          bestLen = len;
          targetPlane = p;
        }
      }
      if (targetPlane < 0) break;
      usedPlane[targetPlane] = true;

      vector<vector<FlightOut>> without = routes;
      without[targetPlane].clear();
      CSRAdj<20000> ciWithout;
      buildCiFromRoutes(without, ciWithout);
      evalCiScore(ciWithout);
      buildReachMaskFromEarliestFast(earliestCi);
      buildFlipMaskForOrigins(originsUsed, originsCount);
      buildActOrigins(originLatest, originW, originsCount);
      auto cand = solveOnePlaneDPTopK(startCities);
      if (!cand.empty() && !sameRoute(cand[0].flights, routes[targetPlane])) {
        auto old = routes[targetPlane];
        routes[targetPlane] = cand[0].flights;
        CSRAdj<20000> ciTry;
        buildCiFromRoutes(routes, ciTry);
        long long scTry = evalCiScore(ciTry);
        if (scTry > bestTrueScore) {
          bestTrueScore = scTry;
          ciAdj = move(ciTry);
        } else {
          routes[targetPlane] = move(old);
        }
      }
    }
  }

  // final debug
  cerr << "[final estimated score] " << bestTrueScore << "\n";

  // output
  for (int p=0;p<K_FIXED;p++){
    cout << routes[p].size() << "\n";
    for (auto &f: routes[p]) {
      cout << (f.a+1) << " " << fmtTimeIdx(f.dep_t) << " "
           << (f.b+1) << " " << fmtTimeIdx(f.arr_t) << "\n";
    }
  }
  return 0;
}
0