#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = (ll)9e18;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
if (!(cin >> n >> m)) return 0;
vector<vector<int>> g(n+1);
// edge: (a,b), color c, cost p
vector<pair<pair<int,int>, pair<int,ll>>> e(m+1);
vector<unordered_map<int,int>> ile(n+1);
vector<unordered_map<int,ll>> suma(n+1);
for (int i = 1; i <= m; ++i) {
int a,b,c; ll p;
cin >> a >> b >> c >> p;
e[i] = {{a,b},{c,p}};
g[a].push_back(i);
g[b].push_back(i);
ile[a][c]++; ile[b][c]++;
suma[a][c] += p; suma[b][c] += p;
}
// dist for being at node v with "no incoming edge" (ind == 0)
vector<ll> distNode(n+1, INF);
// dist for state "we arrived to some node through edge ind"
vector<ll> distEdge(m+1, INF);
// priority queue of tuples: (cost, node v, incoming_edge ind)
// we'll store negative cost to get min-heap behavior with pair
using T = tuple<ll,int,int>;
priority_queue<T> pq;
distNode[1] = 0;
pq.push({0, 1, 0}); // cost 0, at node 1, incoming edge 0
auto kr = [&](int v, int ind) -> pair<int, pair<int,ll>> {
// returns {other_vertex, {color, p}}
if (e[ind].first.first == v) return {e[ind].first.second, e[ind].second};
else return {e[ind].first.first, e[ind].second};
};
while (!pq.empty()) {
auto [negcost, v, ind] = pq.top(); pq.pop();
ll cost = -negcost;
// check whether this tuple matches current best dist
if (ind == 0) {
if (cost != distNode[v]) continue;
} else {
if (cost != distEdge[ind]) continue;
}
// if we reached node n in node-state (ind==0), answer found
if (v == n && ind == 0) {
cout << cost << '\n';
return 0;
}
// precompute incoming color and p if ind != 0
int incoming_color = -1; ll incoming_p = 0;
if (ind != 0) {
auto tmp = kr(v, ind);
incoming_color = tmp.second.first;
incoming_p = tmp.second.second;
}
// explore all incident edges from v
for (int edge_id : g[v]) {
auto u = kr(v, edge_id);
int to = u.first;
int color = u.second.first;
ll p = u.second.second;
// read cnt and sum_total WITHOUT operator[] (use find)
int cnt = 0;
auto itc = ile[v].find(color);
if (itc != ile[v].end()) cnt = itc->second;
ll sum_total = 0;
auto its = suma[v].find(color);
if (its != suma[v].end()) sum_total = its->second;
// if incoming has same color, exclude it from counts (local, no writes)
if (ind != 0 && incoming_color == color) {
cnt -= 1;
sum_total -= incoming_p;
}
if (cnt == 1) {
// unique color -> can go to 'to' as node-state with same cost
if (cost < distNode[to]) {
distNode[to] = cost;
pq.push({-cost, to, 0});
}
} else {
// option 1: go to 'to' with incoming edge state (we traversed edge edge_id),
// cost increases by p (recolor this edge to unique color)
ll newCostEdge = cost + p;
if (newCostEdge < distEdge[edge_id]) {
distEdge[edge_id] = newCostEdge;
pq.push({-newCostEdge, to, edge_id});
}
// option 2: recolor *other* edges of this color at v so that this edge becomes unique
// cost to recolor others = sum_total - p
if (p > (sum_total - p)) {
ll alt = cost + (sum_total - p);
if (alt < distNode[to]) {
distNode[to] = alt;
pq.push({-alt, to, 0});
}
}
}
}
}
// if queue exhausted and no distNode[n] found
if (distNode[n] == INF) cout << -1 << '\n';
else cout << distNode[n] << '\n';
return 0;
}
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |