Submission #1292657

#TimeUsernameProblemLanguageResultExecution timeMemory
1292657LucaDantastimeismoney (balkan11_timeismoney)C++17
100 / 100
290 ms1092 KiB
// AI Generated code for testing if it's right, I already solved it before a few years ago but I wanted to see if AI could solve it



/*
 * Problem: Time is Money (Balkan Olympiad in Informatics 2011)
 * Method: Primal-Dual / Geometry on Convex Hull
 * Complexity: O(K * M * alpha(N)), where K is the number of hull vertices (usually small)
 */

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

// Data structures
struct Edge {
    int u, v, t, c, id;
};

struct Point {
    long long x, y; // x = sumTime, y = sumMoney
};

int N, M;
vector<Edge> edges;

// Global variables to store the best solution found so far
long long minV = -1;
Point bestPoint;
// We store the coefficients (A, B) that produced the best solution
// so we can reconstruct the tree at the end for printing.
long long bestA = 0, bestB = 0;

// Union-Find (DSU) structures
vector<int> parent;

void dsu_init(int n) {
    parent.resize(n);
    for (int i = 0; i < n; ++i) parent[i] = i;
}

int dsu_find(int i) {
    if (parent[i] == i)
        return i;
    return parent[i] = dsu_find(parent[i]);
}

void dsu_union(int i, int j) {
    int root_i = dsu_find(i);
    int root_j = dsu_find(j);
    if (root_i != root_j) {
        parent[root_i] = root_j;
    }
}

// Kruskal's Algorithm with dynamic weights A*t + B*c
// Returns the (SumTime, SumMoney) of the resulting tree
Point getMST(long long A, long long B) {
    // Sort edges based on current weights
    sort(edges.begin(), edges.end(), [A, B](const Edge& a, const Edge& b) {
        long long valA = A * a.t + B * a.c;
        long long valB = A * b.t + B * b.c;
        return valA < valB;
    });

    dsu_init(N);
    long long sumT = 0;
    long long sumC = 0;
    int edgesCount = 0;

    for (const auto& e : edges) {
        if (dsu_find(e.u) != dsu_find(e.v)) {
            dsu_union(e.u, e.v);
            sumT += e.t;
            sumC += e.c;
            edgesCount++;
        }
    }
    
    // Update global minimum if this tree is better
    long long currentV = sumT * sumC;
    if (minV == -1 || currentV < minV || (currentV == minV && sumT < bestPoint.x)) {
        minV = currentV;
        bestPoint = {sumT, sumC};
        bestA = A;
        bestB = B;
    }

    return {sumT, sumC};
}

// Recursive function to find hull points
void solve(Point p1, Point p2) {
    // Calculate weights perpendicular to the line segment p1-p2
    // We use absolute differences for positive weights
    long long A = abs(p1.y - p2.y);
    long long B = abs(p1.x - p2.x);

    // Run MST with new weights
    Point pNew = getMST(A, B);

    // Check if pNew is effectively the same as endpoints (prevents infinite loops)
    if (pNew.x == p1.x && pNew.y == p1.y) return;
    if (pNew.x == p2.x && pNew.y == p2.y) return;

    // Geometry check: Is pNew strictly "below" the segment p1-p2?
    // We check using cross product logic.
    // Vector P1->P2: (p2.x - p1.x, p2.y - p1.y)
    // Vector P1->New: (pNew.x - p1.x, pNew.y - p1.y)
    // Cross product (x1*y2 - x2*y1). If < 0, it's to the right (below/concave).
    long long cross_product = (p2.x - p1.x) * (pNew.y - p1.y) - (p2.y - p1.y) * (pNew.x - p1.x);

    // If cross_product >= 0, the point is not improving the lower hull
    if (cross_product >= 0) return;

    // Recurse
    solve(p1, pNew);
    solve(pNew, p2);
}

int main() {
    // Optimize I/O operations
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    if (fopen("timeismoney.in", "r")) {
        freopen("timeismoney.in", "r", stdin);
        freopen("timeismoney.out", "w", stdout);
    }

    cin >> N >> M;

    for (int i = 0; i < M; ++i) {
        int u, v, t, c;
        cin >> u >> v >> t >> c;
        edges.push_back({u, v, t, c, i});
    }

    // Step 1: Find extreme points
    // Minimize only Time (Weight: 1*t + 0*c)
    Point pTime = getMST(1, 0);
    
    // Minimize only Money (Weight: 0*t + 1*c)
    Point pMoney = getMST(0, 1);

    // Step 2: Start recursive search between the extremes
    solve(pTime, pMoney);

    // Step 3: Output
    cout << bestPoint.x << " " << bestPoint.y << endl;

    // To print the actual edges, we re-run the MST one last time
    // using the cached best weights to reconstruct the specific tree.
    sort(edges.begin(), edges.end(), [](const Edge& a, const Edge& b) {
        long long valA = bestA * a.t + bestB * a.c;
        long long valB = bestA * b.t + bestB * b.c;
        return valA < valB;
    });

    dsu_init(N);
    for (const auto& e : edges) {
        if (dsu_find(e.u) != dsu_find(e.v)) {
            dsu_union(e.u, e.v);
            cout << e.u << " " << e.v << endl;
        }
    }

    return 0;
}

Compilation message (stderr)

timeismoney.cpp: In function 'int main()':
timeismoney.cpp:130:16: warning: ignoring return value of 'FILE* freopen(const char*, const char*, FILE*)' declared with attribute 'warn_unused_result' [-Wunused-result]
  130 |         freopen("timeismoney.in", "r", stdin);
      |         ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
timeismoney.cpp:131:16: warning: ignoring return value of 'FILE* freopen(const char*, const char*, FILE*)' declared with attribute 'warn_unused_result' [-Wunused-result]
  131 |         freopen("timeismoney.out", "w", stdout);
      |         ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Verdict Execution timeMemoryGrader output
Fetching results...