// yukicoder No.5023 Airlines Optimization // Greedy by plane: build k-th plane route by DP on time-expanded graph, // where each flight-edge weight = (approx.) additional wins vs Square when ONLY that edge is added. // // Tuning parameters are at the top (ORIGIN_USE / START_USE / NEAR_CAND / POP_CAND). // Compile: g++ -O2 -std=c++17 -pipe -static -s main.cpp -o main #include 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; // ====== speed/quality knobs (heuristics) ====== // Use only top origins by population when computing edge weights (47 = exact over origins). static constexpr int ORIGIN_USE = 30; // DP start cities (planes can "start" at 06:00 and wait). Larger = more exploration. static constexpr int START_USE = 12; // Candidate destinations per city = NEAR_CAND nearest + POP_CAND most-populous (unique). static constexpr int NEAR_CAND = 6; static constexpr int POP_CAND = 6; static constexpr int K_FIXED = 25; static constexpr int M_FIXED = 400; // Target arrival times: 11:00, 11:30, ..., 21:00 (21 values) static constexpr int TGT = 21; struct Edge { uint8_t to; uint8_t arr_t; // time index [0..180] }; struct FlightOut { int a, b; // 0-based cities int dep_t; // time index int arr_t; // time index }; 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; int 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; } // weights sum table by chunks for fast sum of w[d] over 47-bit mask struct MaskWeighter { vector t0, t1, t2; // 16,16,15 bits uint32_t w[MAXN]{}; void build(const uint32_t* ww) { for (int i=0;i> 16) & 0xFFFFull); uint32_t c = (uint32_t)((mask >> 32) & 0x7FFFull); // 15 bits return t0[a] + t1[b] + t2[c]; } }; // ===== global-ish buffers (fixed sizes) ===== static uint32_t popW[MAXN]; static int N, R; static int durSlot[MAXN][MAXN]; // travel duration in slots (multiple of 5 min) static double distMat[MAXN][MAXN]; static bool validPair[MAXN][MAXN]; // dist >= 0.25R // outEdges per schedule (Square and current Circle) static vector outSq[V]; static vector outCi[V]; // earliestDest[dest][city][time] = min arrival time index at dest starting from (city,time), INF if unreachable static uint16_t earliestSq[MAXN][MAXN][TS]; static uint16_t earliestCi[MAXN][MAXN][TS]; static constexpr uint16_t INF16 = 65535; // latestStart[origin][dest][deadline] = latest start time index to reach dest by deadline, -1 if impossible static int16_t latestSq[MAXN][MAXN][TS]; static int16_t latestCi[MAXN][MAXN][TS]; // for targets: s_sq[origin][dest][tgtIdx], s_ci similarly (time indices, -1 if unreachable) static int16_t s_sq[MAXN][MAXN][TGT]; static int16_t s_ci[MAXN][MAXN][TGT]; // reachMask[vertex][tgt] = destinations reachable by target arrival time from vertex (city,time) on current circle schedule static uint64_t reachMask[V][TGT]; // oldWin[origin][dest][tgt] = (circle already wins) on current schedule (before adding k-th plane) static bool oldWin[MAXN][MAXN][TGT]; // flipMask[originUsedIndex][tgt][startTime] = destinations that would flip (not already win, and startTime > s_sq) // (47-bit mask over destinations) static uint64_t flipMask[ORIGIN_USE][TGT][TS]; // target arrival indices static int tgtIdx[TGT]; // helpers static MaskWeighter weighter; // ===== Build earliest and latest arrays for a schedule ===== static void buildEarliestAndLatest(const vector outEdges[V], uint16_t earliest[MAXN][MAXN][TS], int16_t latest[MAXN][MAXN][TS]) { // earliest for (int d=0; d=0; t--) { for (int c=0; c sq); } } } } // originsUsed: indices of origins (0-based), size O (<=ORIGIN_USE) static void buildFlipMaskForOrigins(const vector& originsUsed) { for (int oi=0; oi<(int)originsUsed.size(); oi++) { int o = originsUsed[oi]; for (int ti=0; ti sq if (thr <= TS-1) addAt[thr] |= (1ull << d); } uint64_t cur=0; for (int s=0; s& originsUsed) { int vtx = toCity*TS + arrT; uint64_t gain = 0; // loop originsUsed for (int oi=0; oi<(int)originsUsed.size(); oi++) { int o = originsUsed[oi]; int16_t sIdx = latestCi[o][fromCity][depT]; // latest departure to reach fromCity by depT if (sIdx < 0) continue; uint64_t wo = popW[o]; // loop target times for (int ti=0; ti solveOnePlaneDP(const vector>& candDest, const vector& startCities, const vector& originsUsed) { static __int128 dp[V]; static int prevv[V]; const __int128 NEG = -((__int128)1<<120); for (int i=0;i bestVal) { bestVal=cur; bestVtx=u; } // wait if (t+1 < TS) { int v = u+1; if (cur > dp[v]) { dp[v]=cur; prevv[v]=u; } } // flights (candidate destinations only) for (int j: candDest[c]) { int dt = durSlot[c][j]; int at = t + dt; if (at >= TS) continue; int v = j*TS + at; uint64_t g = edgeGain(c, t, j, at, originsUsed); __int128 nv = cur + (__int128)g; if (nv > dp[v]) { dp[v]=nv; prevv[v]=u; } } } } // take global best end for (int u=0;u bestVal) { bestVal=dp[u]; bestVtx=u; } } // reconstruct path vector revFlights; int cur = bestVtx; 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) { // flight revFlights.push_back(FlightOut{pc, cc, pt, ct}); } cur = p; } reverse(revFlights.begin(), revFlights.end()); return revFlights; } // ===== score evaluation (debug) ===== static long long evalScoreEstimate() { // compute current circle latest + s_ci already built outside if called appropriately __int128 vci=0, vsq=0; for (int o=0;o sq) vci += ww; else vsq += ww; } } } if (vci+vsq==0) return 0; long double S = (long double)vci / (long double)(vci+vsq); long long score = (long long)floor(1e6L * S + 1e-12L); return score; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> N >> R; vector xs(N), ys(N); for (int i=0;i> xs[i] >> ys[i] >> popW[i]; } // target indices for (int i=0;i= 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); int durMin = q * 5; durSlot[i][j] = durMin / 5; } } } // build weight sum tables weighter.build(popW); int M; cin >> M; for (int i=0;i> a >> ss >> b >> tt; --a; --b; int s = timeToIdx(parseTimeStr(ss)); int t = timeToIdx(parseTimeStr(tt)); outSq[a*TS + s].push_back(Edge{(uint8_t)b,(uint8_t)t}); } int K; cin >> K; // should be 25 (void)K; // current circle schedule initially empty for (int i=0;i popOrder(N); iota(popOrder.begin(), popOrder.end(), 0); sort(popOrder.begin(), popOrder.end(), [&](int a,int b){ return popW[a]>popW[b]; }); vector> candDest(N); for (int i=0;i> near; near.reserve(N-1); for (int j=0;j cand; cand.reserve(NEAR_CAND + POP_CAND + 4); // nearest for (int k=0;k<(int)near.size() && (int)cand.size() startCities; for (int i=0;i originsUsed; for (int i=0;i> routes(K_FIXED); for (int plane=0; plane