#include #include #include #include #include // Use long long for costs and total sum as prices can be large (2^30) // and sum of prices can exceed 32-bit integer limit. const long long INF = 1e18; // A large enough value for infinity struct Edge { int to; // Destination vertex int capacity; // Capacity of the edge int flow; // Current flow through the edge long long cost; // Cost per unit of flow int rev; // Index of the reverse edge in adj[to] }; std::vector> adj; // Adjacency list for the graph std::vector dist; // Shortest distance from source in SPFA std::vector parent_v; // Parent vertex in shortest path std::vector parent_e; // Parent edge index in shortest path int V_flow; // Total number of vertices in the flow network // Adds an edge and its residual edge void add_edge(int u, int v, int cap, long long cost) { adj[u].push_back({v, cap, 0, cost, (int)adj[v].size()}); adj[v].push_back({u, 0, 0, -cost, (int)adj[u].size() - 1}); // Residual edge for 0 cap initially } // Min-cost max-flow algorithm using SPFA // Finds paths and augments flow as long as it reduces total cost (increases profit) // total_flow_val will store the number of matched pairs. long long min_cost_max_flow_calc(int s, int t, int& total_flow_val) { long long total_cost = 0; total_flow_val = 0; dist.resize(V_flow); parent_v.resize(V_flow); parent_e.resize(V_flow); while (true) { std::fill(dist.begin(), dist.end(), INF); dist[s] = 0; std::vector in_queue(V_flow, false); std::queue q; q.push(s); in_queue[s] = true; // SPFA to find shortest path in terms of cost while (!q.empty()) { int u = q.front(); q.pop(); in_queue[u] = false; for (int i = 0; i < adj[u].size(); ++i) { Edge& e = adj[u][i]; if (e.capacity - e.flow > 0 && dist[e.to] > dist[u] + e.cost) { dist[e.to] = dist[u] + e.cost; parent_v[e.to] = u; parent_e[e.to] = i; if (!in_queue[e.to]) { q.push(e.to); in_queue[e.to] = true; } } } } // If no path found, or path found has non-negative cost (no more profit) if (dist[t] == INF || dist[t] >= 0) { // dist[t] >= 0 means we can't add more items to increase profit // (as costs are -price, so positive cost means negative profit) break; } // All edge capacities for matching are 1. So path_flow is 1. int path_flow = 1; total_flow_val += path_flow; // One more item matched total_cost += (long long)path_flow * dist[t]; // Add cost of this path to total // Augment flow along the path int cur = t; while (cur != s) { int prev = parent_v[cur]; int edge_idx = parent_e[cur]; adj[prev][edge_idx].flow += path_flow; adj[cur][adj[prev][edge_idx].rev].flow -= path_flow; // Update flow on reverse edge cur = prev; } } return total_cost; } struct BidInfo { int x_orig, y_orig, z; // Original buyer ID, item ID, and Z value long long price; // Calculated price 2^Z }; int main() { std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); int N_max, M_max, K_val, L_bids; // Max IDs N, M, max power K, num_bids L std::cin >> N_max >> M_max >> K_val >> L_bids; std::vector bids(L_bids); std::map buyer_orig_to_idx; // Map original buyer ID to 0-indexed internal ID std::map item_orig_to_idx; // Map original item ID to 0-indexed internal ID int next_buyer_idx = 0; int next_item_idx = 0; for (int i = 0; i < L_bids; ++i) { std::cin >> bids[i].x_orig >> bids[i].y_orig >> bids[i].z; bids[i].price = (1LL << bids[i].z); // Price is 2^Z // Assign new compact index if this buyer/item ID is seen for the first time if (buyer_orig_to_idx.find(bids[i].x_orig) == buyer_orig_to_idx.end()) { buyer_orig_to_idx[bids[i].x_orig] = next_buyer_idx++; } if (item_orig_to_idx.find(bids[i].y_orig) == item_orig_to_idx.end()) { item_orig_to_idx[bids[i].y_orig] = next_item_idx++; } } int num_actual_buyers = next_buyer_idx; int num_actual_items = next_item_idx; // Define source and sink nodes for the flow network // Buyers: 0 to num_actual_buyers-1 // Items: num_actual_buyers to num_actual_buyers + num_actual_items - 1 int s_node = num_actual_buyers + num_actual_items; // Source node index int t_node = num_actual_buyers + num_actual_items + 1; // Sink node index V_flow = num_actual_buyers + num_actual_items + 2; // Total vertices in flow network adj.assign(V_flow, std::vector()); // Initialize adjacency list // Edges from source to buyers for (int i = 0; i < num_actual_buyers; ++i) { add_edge(s_node, i, 1, 0); // cap 1, cost 0 } // Edges from items to sink for (int i = 0; i < num_actual_items; ++i) { // Item node index is i (0 to num_actual_items-1) + num_actual_buyers add_edge(num_actual_buyers + i, t_node, 1, 0); // cap 1, cost 0 } // Edges from buyers to items for each bid for (const auto& bid : bids) { int u_compact_idx = buyer_orig_to_idx[bid.x_orig]; int v_compact_idx = item_orig_to_idx[bid.y_orig] + num_actual_buyers; // Offset item indices add_edge(u_compact_idx, v_compact_idx, 1, -bid.price); // cap 1, cost -price } int total_flow = 0; // Will store total number of items sold long long min_total_cost = min_cost_max_flow_calc(s_node, t_node, total_flow); // Max revenue is -min_total_cost (because costs were negative prices) std::cout << -min_total_cost << std::endl; return 0; }