#include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr int START_TIME = 6 * 60; constexpr int END_TIME = 21 * 60; constexpr int SCORE_START = 11 * 60; constexpr int SCORE_INTERVAL = 30; constexpr int DEADLINE_COUNT = 21; constexpr int SOURCE_CAND_LIMIT = 46; constexpr uint32_t RNG_SEED = 20260225u; constexpr int DESTROY_MIN_PLANES = 2; constexpr int DESTROY_MAX_PLANES = 4; constexpr double REPAIR_TIME_LIMIT_SEC = 0.90; constexpr int NEG_INF = -1'000'000'000; struct City { int x = 0; int y = 0; long long w = 0; }; struct Flight { int from = -1; int to = -1; int dep = -1; int arr = -1; }; struct Candidate { bool valid = false; long long gain = std::numeric_limits::min(); int from = -1; int to = -1; int dep = -1; int arr = -1; }; struct PlaneBuildResult { std::vector route; long long gain = 0; }; int parse_time(const std::string& s) { return std::stoi(s.substr(0, 2)) * 60 + std::stoi(s.substr(3, 2)); } std::string format_time(int t) { char buf[6]; std::snprintf(buf, sizeof(buf), "%02d:%02d", t / 60, t % 60); return std::string(buf); } int idx3(int n, int i, int j, int k) { return (i * n + j) * DEADLINE_COUNT + k; } bool is_better_candidate(const Candidate& a, const Candidate& b) { if (!b.valid) { return true; } const int da = a.arr - a.dep; const int db = b.arr - b.dep; const __int128 lhs = static_cast<__int128>(a.gain) * static_cast<__int128>(db); const __int128 rhs = static_cast<__int128>(b.gain) * static_cast<__int128>(da); if (lhs != rhs) { return lhs > rhs; } if (a.gain != b.gain) { return a.gain > b.gain; } if (a.dep != b.dep) { return a.dep > b.dep; } if (a.arr != b.arr) { return a.arr > b.arr; } if (a.to != b.to) { return a.to < b.to; } return a.from < b.from; } bool parse_bool_token(const std::string& s, bool& out) { std::string v = s; std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); if (v == "1" || v == "on" || v == "true") { out = true; return true; } if (v == "0" || v == "off" || v == "false") { out = false; return true; } return false; } long long estimate_score_like_objective( int n, const std::vector>& weight, const std::vector& sq_latest, const std::vector& ci_latest ) { long long v_ci = 0; long long v_sq = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { const long long w = weight[i][j]; if (w == 0) { continue; } for (int k = 0; k < DEADLINE_COUNT; ++k) { const int p = idx3(n, i, j, k); if (ci_latest[p] > sq_latest[p]) { v_ci += w; } else { v_sq += w; } } } } if (v_ci + v_sq == 0) { return 0; } return static_cast((1'000'000.0L * static_cast(v_ci)) / static_cast(v_ci + v_sq)); } bool flight_desc_order(const Flight& a, const Flight& b) { if (a.dep != b.dep) { return a.dep > b.dep; } if (a.arr != b.arr) { return a.arr > b.arr; } if (a.to != b.to) { return a.to < b.to; } return a.from < b.from; } std::vector compute_ci_latest_exact( int n, const std::vector& flights_sorted, const std::vector& deadlines ) { std::vector ci_latest(n * n * DEADLINE_COUNT, NEG_INF); std::vector latest(n, NEG_INF); for (int dest = 0; dest < n; ++dest) { for (int d = 0; d < DEADLINE_COUNT; ++d) { std::fill(latest.begin(), latest.end(), NEG_INF); latest[dest] = deadlines[d]; for (const Flight& f : flights_sorted) { if (latest[f.to] >= f.arr && latest[f.from] < f.dep) { latest[f.from] = f.dep; } } for (int src = 0; src < n; ++src) { ci_latest[idx3(n, src, dest, d)] = latest[src]; } } } return ci_latest; } std::vector compute_latest_to_target_deadline( int n, const std::vector& flights_sorted, int target, int deadline ) { std::vector latest(n, NEG_INF); latest[target] = deadline; for (const Flight& f : flights_sorted) { if (latest[f.to] >= f.arr && latest[f.from] < f.dep) { latest[f.from] = f.dep; } } return latest; } PlaneBuildResult build_plane_greedy( bool snap_arrival_to_30, const std::vector>& nearest_sources, const std::vector>& duration, const std::vector>& weight, const std::vector& sq_latest, const std::vector& deadlines, std::vector& flights_sorted, std::vector& ci_latest ) { const int n = static_cast(duration.size()); PlaneBuildResult result; std::vector rev_route; long long plane_gain = 0; auto evaluate_candidate_gain = [&](int from, int to, int dep, int arr) { if (dep < START_TIME || arr > END_TIME) { return 0LL; } std::vector> downstream_states; downstream_states.reserve(n * DEADLINE_COUNT); for (int dest = 0; dest < n; ++dest) { bool used = false; for (int src = 0; src < n; ++src) { if (weight[src][dest] > 0) { used = true; break; } } if (!used) { continue; } for (int k = 0; k < DEADLINE_COUNT; ++k) { if (ci_latest[idx3(n, to, dest, k)] >= arr) { downstream_states.push_back({dest, k}); } } } if (downstream_states.empty()) { return 0LL; } const std::vector latest_to_from = compute_latest_to_target_deadline(n, flights_sorted, from, dep); long long gain = 0; for (int src = 0; src < n; ++src) { const int candidate_depart = latest_to_from[src]; if (candidate_depart <= NEG_INF / 2) { continue; } for (const auto& [dest, k] : downstream_states) { const int p = idx3(n, src, dest, k); if (candidate_depart > sq_latest[p] && candidate_depart > ci_latest[p]) { gain += weight[src][dest]; } } } return gain; }; auto push_flight_and_recompute = [&](const Flight& f) { const auto it = std::lower_bound(flights_sorted.begin(), flights_sorted.end(), f, flight_desc_order); flights_sorted.insert(it, f); ci_latest = compute_ci_latest_exact(n, flights_sorted, deadlines); }; Candidate best_init; const int init_arrival = END_TIME; for (int terminal = 0; terminal < n; ++terminal) { for (const int from : nearest_sources[terminal]) { const int dep = init_arrival - duration[from][terminal]; if (dep < START_TIME) { continue; } Candidate cand; cand.valid = true; cand.from = from; cand.to = terminal; cand.dep = dep; cand.arr = init_arrival; cand.gain = evaluate_candidate_gain(cand.from, cand.to, cand.dep, cand.arr); if (is_better_candidate(cand, best_init)) { best_init = cand; } } } if (!best_init.valid || best_init.gain <= 0) { return result; } const Flight init_flight{best_init.from, best_init.to, best_init.dep, best_init.arr}; push_flight_and_recompute(init_flight); rev_route.push_back(init_flight); plane_gain += best_init.gain; int head_city = best_init.from; int head_departure = best_init.dep; while (true) { if (head_departure < START_TIME) { break; } Candidate best_next; int fixed_arrival = head_departure; if (snap_arrival_to_30) { fixed_arrival = ((head_departure + 15) / 30) * 30; fixed_arrival = std::min(fixed_arrival, END_TIME); if (fixed_arrival > head_departure) { fixed_arrival -= 30; } } if (fixed_arrival < START_TIME) { break; } for (const int from : nearest_sources[head_city]) { const int dep = fixed_arrival - duration[from][head_city]; if (dep < START_TIME) { continue; } Candidate cand; cand.valid = true; cand.from = from; cand.to = head_city; cand.dep = dep; cand.arr = fixed_arrival; cand.gain = evaluate_candidate_gain(cand.from, cand.to, cand.dep, cand.arr); if (is_better_candidate(cand, best_next)) { best_next = cand; } } if (!best_next.valid || best_next.gain <= 0) { break; } const Flight next_flight{best_next.from, head_city, best_next.dep, best_next.arr}; push_flight_and_recompute(next_flight); rev_route.push_back(next_flight); plane_gain += best_next.gain; head_city = best_next.from; head_departure = best_next.dep; } std::reverse(rev_route.begin(), rev_route.end()); result.route = std::move(rev_route); result.gain = plane_gain; return result; } } // namespace int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); bool snap_arrival_to_30 = false; if (const char* env = std::getenv("ARRIVAL_30"); env != nullptr) { bool parsed = false; if (!parse_bool_token(env, parsed)) { std::cerr << "invalid ARRIVAL_30: " << env << "\n"; return 1; } snap_arrival_to_30 = parsed; } int n = 0; int r = 0; std::cin >> n >> r; std::vector cities(n); for (int i = 0; i < n; ++i) { std::cin >> cities[i].x >> cities[i].y >> cities[i].w; } int m = 0; std::cin >> m; std::vector sq_flights(m); for (int i = 0; i < m; ++i) { std::string s; std::string t; std::cin >> sq_flights[i].from >> s >> sq_flights[i].to >> t; --sq_flights[i].from; --sq_flights[i].to; sq_flights[i].dep = parse_time(s); sq_flights[i].arr = parse_time(t); } int k_plane = 0; std::cin >> k_plane; std::vector deadlines(DEADLINE_COUNT); for (int k = 0; k < DEADLINE_COUNT; ++k) { deadlines[k] = SCORE_START + SCORE_INTERVAL * k; } std::vector> duration(n, std::vector(n, 0)); std::vector> weight(n, std::vector(n, 0)); std::vector> nearest_sources(n); const long double threshold = static_cast(r) * 0.25L; for (int dest = 0; dest < n; ++dest) { std::vector> cand; cand.reserve(n - 1); for (int src = 0; src < n; ++src) { if (src == dest) { continue; } const long long dx = static_cast(cities[src].x) - cities[dest].x; const long long dy = static_cast(cities[src].y) - cities[dest].y; const long long d2 = dx * dx + dy * dy; cand.push_back({d2, src}); } std::sort(cand.begin(), cand.end(), [](const auto& a, const auto& b) { if (a.first != b.first) { return a.first < b.first; } return a.second < b.second; }); const int lim = std::min(SOURCE_CAND_LIMIT, static_cast(cand.size())); nearest_sources[dest].reserve(lim); for (int i = 0; i < lim; ++i) { nearest_sources[dest].push_back(cand[i].second); } } for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (i == j) { continue; } const long double dx = static_cast(cities[i].x - cities[j].x); const long double dy = static_cast(cities[i].y - cities[j].y); const long double dist = std::sqrt(dx * dx + dy * dy); const long double raw = 60.0L * dist / 800.0L + 40.0L; duration[i][j] = static_cast(std::ceil((raw - 1e-12L) / 5.0L)) * 5; if (dist + 1e-12L >= threshold) { weight[i][j] = cities[i].w * cities[j].w; } } } std::sort(sq_flights.begin(), sq_flights.end(), [](const Flight& a, const Flight& b) { if (a.dep != b.dep) { return a.dep > b.dep; } if (a.arr != b.arr) { return a.arr > b.arr; } if (a.to != b.to) { return a.to < b.to; } return a.from < b.from; }); std::vector sq_latest(n * n * DEADLINE_COUNT, NEG_INF); std::vector latest(n, NEG_INF); for (int dest = 0; dest < n; ++dest) { for (int d = 0; d < DEADLINE_COUNT; ++d) { std::fill(latest.begin(), latest.end(), NEG_INF); latest[dest] = deadlines[d]; for (const Flight& f : sq_flights) { if (latest[f.to] >= f.arr && latest[f.from] < f.dep) { latest[f.from] = f.dep; } } for (int src = 0; src < n; ++src) { sq_latest[idx3(n, src, dest, d)] = latest[src]; } } } std::vector current_flights_sorted; std::vector ci_latest = compute_ci_latest_exact(n, current_flights_sorted, deadlines); std::vector> answer(k_plane); std::cerr << "arrival30=" << (snap_arrival_to_30 ? "on" : "off") << "\n"; // 初期解: 全機体を通常貪欲で構築 for (int plane_id = 0; plane_id < k_plane; ++plane_id) { PlaneBuildResult built = build_plane_greedy( snap_arrival_to_30, nearest_sources, duration, weight, sq_latest, deadlines, current_flights_sorted, ci_latest ); answer[plane_id] = std::move(built.route); std::cerr << "plane " << (plane_id + 1) << ": flights=" << answer[plane_id].size() << ", gain=" << built.gain << ", approx_score=" << estimate_score_like_objective(n, weight, sq_latest, ci_latest) << "\n"; } long long current_score = estimate_score_like_objective(n, weight, sq_latest, ci_latest); std::cerr << "initial_score=" << current_score << "\n"; // 破壊再構築: ランダムに 2〜4 機を白紙に戻して再貪欲 std::mt19937 rng(RNG_SEED); std::uniform_int_distribution destroy_cnt_dist(DESTROY_MIN_PLANES, DESTROY_MAX_PLANES); std::vector perm(k_plane); std::iota(perm.begin(), perm.end(), 0); int iterations = 0; int accepted = 0; const auto repair_start = std::chrono::steady_clock::now(); while (true) { const auto now = std::chrono::steady_clock::now(); const double elapsed = std::chrono::duration(now - repair_start).count(); if (elapsed >= REPAIR_TIME_LIMIT_SEC) { break; } ++iterations; std::shuffle(perm.begin(), perm.end(), rng); const int destroy_cnt = std::min(k_plane, destroy_cnt_dist(rng)); std::vector destroyed_planes(perm.begin(), perm.begin() + destroy_cnt); std::vector destroyed(k_plane, 0); for (int p : destroyed_planes) { destroyed[p] = 1; } std::vector trial_flights; for (int plane_id = 0; plane_id < k_plane; ++plane_id) { if (destroyed[plane_id]) { continue; } for (const Flight& f : answer[plane_id]) { trial_flights.push_back(f); } } std::sort(trial_flights.begin(), trial_flights.end(), flight_desc_order); std::vector trial_ci_latest = compute_ci_latest_exact(n, trial_flights, deadlines); std::vector> rebuilt_routes(destroy_cnt); for (int i = 0; i < destroy_cnt; ++i) { PlaneBuildResult rebuilt = build_plane_greedy( snap_arrival_to_30, nearest_sources, duration, weight, sq_latest, deadlines, trial_flights, trial_ci_latest ); rebuilt_routes[i] = std::move(rebuilt.route); } const long long trial_score = estimate_score_like_objective(n, weight, sq_latest, trial_ci_latest); if (trial_score > current_score) { ++accepted; for (int i = 0; i < destroy_cnt; ++i) { answer[destroyed_planes[i]] = std::move(rebuilt_routes[i]); } current_flights_sorted = std::move(trial_flights); ci_latest.swap(trial_ci_latest); current_score = trial_score; std::cerr << "improve iter=" << iterations << " score=" << current_score << " destroy=" << destroy_cnt << " accepted=" << accepted << "\n"; } } std::cerr << "repair_done iter=" << iterations << " accepted=" << accepted << " final_score=" << current_score << "\n"; for (int plane_id = 0; plane_id < k_plane; ++plane_id) { const auto& route = answer[plane_id]; std::cout << route.size() << "\n"; for (const Flight& f : route) { std::cout << (f.from + 1) << ' ' << format_time(f.dep) << ' ' << (f.to + 1) << ' ' << format_time(f.arr) << "\n"; } } return 0; }