結果
問題 |
No.2909 Imaginary Summer
|
ユーザー |
|
提出日時 | 2025-02-01 19:19:02 |
言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 341 ms / 6,000 ms |
コード長 | 7,721 bytes |
コンパイル時間 | 4,895 ms |
コンパイル使用メモリ | 297,912 KB |
実行使用メモリ | 28,392 KB |
最終ジャッジ日時 | 2025-02-01 19:19:22 |
合計ジャッジ時間 | 18,395 ms |
ジャッジサーバーID (参考情報) |
judge3 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 37 |
ソースコード
// GPTo3-mini-highによる生成コードです。 #include <bits/stdc++.h> using namespace std; // 構造体 Station:各駅の座標とホテルからの最短到達時間 D を保持 struct Station { double x, y; double d; // ホテルから駅までの最短時間 }; // kd-tree のノード struct KdNode { double x, y, d; // 点の座標と,その点に対応する D 値 double minX, maxX, minY, maxY; // この部分木に含まれる点のバウンディングボックス double subMinD; // 部分木中の最小 D 値 KdNode *left, *right; }; // kd-tree を再帰的に構築する関数 // pts の [l, r) の区間から構築.分割軸は depth % 2 (0: x軸,1: y軸) KdNode* buildKdTree(vector<Station> &pts, int l, int r, int depth) { if(l >= r) return nullptr; int axis = depth & 1; // 0: x軸,1: y軸 int mid = (l + r) / 2; if(axis == 0) { nth_element(pts.begin() + l, pts.begin() + mid, pts.begin() + r, [](const Station &a, const Station &b){ return a.x < b.x; }); } else { nth_element(pts.begin() + l, pts.begin() + mid, pts.begin() + r, [](const Station &a, const Station &b){ return a.y < b.y; }); } KdNode* node = new KdNode(); node->x = pts[mid].x; node->y = pts[mid].y; node->d = pts[mid].d; node->left = buildKdTree(pts, l, mid, depth+1); node->right = buildKdTree(pts, mid+1, r, depth+1); // 自身の点でバウンディングボックスを初期化 node->minX = node->maxX = node->x; node->minY = node->maxY = node->y; node->subMinD = node->d; if(node->left){ node->minX = min(node->minX, node->left->minX); node->maxX = max(node->maxX, node->left->maxX); node->minY = min(node->minY, node->left->minY); node->maxY = max(node->maxY, node->left->maxY); node->subMinD = min(node->subMinD, node->left->subMinD); } if(node->right){ node->minX = min(node->minX, node->right->minX); node->maxX = max(node->maxX, node->right->maxX); node->minY = min(node->minY, node->right->minY); node->maxY = max(node->maxY, node->right->maxY); node->subMinD = min(node->subMinD, node->right->subMinD); } return node; } // ユークリッド距離を計算する補助関数 inline double distPoint(double ax, double ay, double bx, double by) { double dx = ax - bx, dy = ay - by; return sqrt(dx*dx + dy*dy); } // kd-tree のノードのバウンディングボックスとクエリ点 (qx,qy) との最小距離を返す. double boxDistance(KdNode* node, double qx, double qy) { double dx = 0.0, dy = 0.0; if(qx < node->minX) dx = node->minX - qx; else if(qx > node->maxX) dx = qx - node->maxX; if(qy < node->minY) dy = node->minY - qy; else if(qy > node->maxY) dy = qy - node->maxY; return sqrt(dx*dx + dy*dy); } // kd-tree探索の際のグローバル変数:現在の最良候補値 double bestCandidate; // kd-tree を用いて,関数 f(p) = (p.d + distance(query, p)) の最小値を求める. void queryKdTree(KdNode* node, double qx, double qy) { if(!node) return; // このノードの部分木内のどの点についても, f >= (subMinD + バウンディングボックスまでの距離) となる double lowerBound = node->subMinD + boxDistance(node, qx, qy); if(lowerBound >= bestCandidate) return; // 現在のノードの点について計算 double curVal = node->d + distPoint(node->x, node->y, qx, qy); bestCandidate = min(bestCandidate, curVal); double leftLB = node->left ? (node->left->subMinD + boxDistance(node->left, qx, qy)) : numeric_limits<double>::infinity(); double rightLB = node->right ? (node->right->subMinD + boxDistance(node->right, qx, qy)) : numeric_limits<double>::infinity(); if(leftLB < rightLB){ if(leftLB < bestCandidate) queryKdTree(node->left, qx, qy); if(rightLB < bestCandidate) queryKdTree(node->right, qx, qy); } else { if(rightLB < bestCandidate) queryKdTree(node->right, qx, qy); if(leftLB < bestCandidate) queryKdTree(node->left, qx, qy); } } // メイン関数 int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int N, M, K; cin >> N >> M >> K; double P, Q; cin >> P >> Q; // 駅の座標を入力 (N 個) vector<pair<double,double>> stationCoords(N); for (int i = 0; i < N; i++){ double x, y; cin >> x >> y; stationCoords[i] = {x, y}; } // 目的地の座標を入力 (K 個) vector<pair<double,double>> destinations(K); for (int i = 0; i < K; i++){ double a, b; cin >> a >> b; destinations[i] = {a, b}; } // V の値を入力(ほぼ 10^18) long long V_ll; cin >> V_ll; double V = (double) V_ll; // 駅間を結ぶ鉄道路線 (M 本) のグラフを作成. // 各路線は駅 u と v を結び,重みは (ユークリッド距離) ÷ V となる. vector<vector<pair<int,double>>> graph(N); for (int i = 0; i < M; i++){ int u, v; cin >> u >> v; u--; v--; // 0-indexed に変換 double dx = stationCoords[u].first - stationCoords[v].first; double dy = stationCoords[u].second - stationCoords[v].second; double segLen = sqrt(dx*dx + dy*dy); double w = segLen / V; graph[u].push_back({v, w}); graph[v].push_back({u, w}); } // 各駅 j について, // ホテルから駅 j へ徒歩で向かった場合の時間を初期値とし, // 鉄道を使った場合の最短到達時間 D[j] をマルチソース Dijkstra で計算する. vector<double> D(N, numeric_limits<double>::infinity()); priority_queue<pair<double,int>, vector<pair<double,int>>, greater<pair<double,int>>> pq; for (int j = 0; j < N; j++){ double dx = stationCoords[j].first - P; double dy = stationCoords[j].second - Q; double d0 = sqrt(dx*dx + dy*dy); D[j] = d0; pq.push({d0, j}); } while(!pq.empty()){ auto cur = pq.top(); pq.pop(); double curD = cur.first; int u = cur.second; if(curD > D[u] + 1e-12) continue; for(auto &edge : graph[u]){ int v = edge.first; double nd = curD + edge.second; if(nd < D[v] - 1e-12){ D[v] = nd; pq.push({nd, v}); } } } // kd-tree のために,各駅を Station 型としてまとめる. vector<Station> pts(N); for (int j = 0; j < N; j++){ pts[j].x = stationCoords[j].first; pts[j].y = stationCoords[j].second; pts[j].d = D[j]; } KdNode* kdRoot = buildKdTree(pts, 0, N, 0); // 各目的地について, // 片道の最短時間は,直接徒歩の場合と // 「ホテル→(徒歩+鉄道)→ある駅→徒歩→目的地」のどちらか短い方で, // 往復なので 2 倍して総和を求める. double totalTime = 0.0; for (int i = 0; i < K; i++){ double ax = destinations[i].first, ay = destinations[i].second; double direct = distPoint(P, Q, ax, ay); // ホテルから目的地まで直接徒歩の場合の時間 bestCandidate = numeric_limits<double>::infinity(); // kd-tree を用いて,min_{j} { D[j] + distance( (X[j],Y[j]), (ax,ay) ) } を求める queryKdTree(kdRoot, ax, ay); double bestRoute = min(direct, bestCandidate); totalTime += 2.0 * bestRoute; } cout << fixed << setprecision(10) << totalTime << "\n"; return 0; }