Submission #1151424

#TimeUsernameProblemLanguageResultExecution timeMemory
1151424aintaReconstruction Project (JOI22_reconstruction)C++20
35 / 100
5094 ms13292 KiB
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

// ----- Edge structure -----
struct Edge {
    int u, v;
    ll w;
    int id; // original id (0-indexed)
};

// ----- Global Variables -----
int N, M;
vector<Edge> edges; // original edges
// We'll compute Lbound and Rbound for each edge (indexed by its original id)
vector<ll> Lbound, Rbound;
 
// ----- DFS in Tree T -----
// T is maintained as an adjacency list: for each node, a list of (neighbor, edge id)
bool dfsPath(int cur, int target, int parent, vector<int> &path) {
    if(cur == target) return true;
    for(auto &p : path); // dummy
    // We'll pass T as a global reference below.
    return false;
}
 
// We'll write a DFS that finds the unique path from 's' to 't' in tree T.
// It stores in 'path' the edge ids (from T) on the path.
bool findPath(int cur, int target, int parent, const vector<vector<pair<int,int>>>& T, vector<int> &path) {
    if(cur == target) return true;
    for(auto &pr : T[cur]) {
        int nxt = pr.first, edgeId = pr.second;
        if(nxt == parent) continue;
        if(findPath(nxt, target, cur, T, path)) {
            path.push_back(edgeId);
            return true;
        }
    }
    return false;
}
 
// ----- Lower Bound Computation -----
// Process edges in increasing order. Maintain tree T as an undirected spanning tree (as an adjacency list).
// For each edge (in sorted order) with endpoints u and v:
//   - If u and v are not connected in T (found by DFS), then set Lbound = 1 and add the edge into T.
//   - Otherwise, find the unique path in T between u and v, let j be the edge on that path with maximum weight.
//     Then set Lbound = (w_i + w_j) / 2 + 1.
//     Also, if w_i < w_j, then remove edge j from T and add edge i.
void computeLowerBounds() {
    Lbound.assign(M, 0);
    // T: tree maintained as adjacency list; T[node] = vector of (neighbor, edge id)
    vector<vector<pair<int,int>>> T(N);
    
    // Sort edges in increasing order by weight.
    vector<Edge> sorted = edges;
    sort(sorted.begin(), sorted.end(), [](const Edge &a, const Edge &b){
        return a.w < b.w;
    });
    
    // For connectivity checking we use DFS in T.
    auto connected = [&](int s, int t) -> bool {
        vector<bool> vis(N, false);
        function<bool(int)> dfs = [&](int cur) -> bool {
            if(cur == t) return true;
            vis[cur] = true;
            for(auto &pr : T[cur]) {
                int nxt = pr.first;
                if(!vis[nxt] && dfs(nxt))
                    return true;
            }
            return false;
        };
        return dfs(s);
    };
    
    // For each edge in sorted order, process it.
    for(auto &e : sorted) {
        int u = e.u, v = e.v;
        if(!connected(u, v)) {
            // Not connected: edge e is critical for small X.
            Lbound[e.id] = 1;
            // Add e to T:
            T[u].push_back({v, e.id});
            T[v].push_back({u, e.id});
        } else {
            // u and v are already connected in T.
            // Find the unique path from u to v in T.
            vector<int> pathEdgeIds;
            findPath(u, v, -1, T, pathEdgeIds);
            // The path is stored in reverse order; we scan all edge ids in the path.
            int maxEdgeId = -1;
            ll minW = 1e18;
            for (int id : pathEdgeIds) {
                ll wtemp = edges[id].w; // original weight of that edge.
                if(wtemp < minW) { minW = wtemp; maxEdgeId = id; }
            }
            // Compute lower bound for current edge:
            // According to the idea: L_val = (w_i + w_j) / 2 + 1.
            Lbound[e.id] = (e.w + minW) / 2 + 1;
            // Additionally, if current edge is strictly lighter than the one on the path,
            // then we can replace that heavier edge in T with the current edge.
                // Remove edge maxEdgeId from T.
                int a = edges[maxEdgeId].u, b = edges[maxEdgeId].v;
                auto removeEdge = [&](int x, int y, int eid) {
                    for(auto it = T[x].begin(); it != T[x].end(); ++it) {
                        if(it->first == y && it->second == eid) {
                            T[x].erase(it);
                            break;
                        }
                    }
                };
                removeEdge(a, b, maxEdgeId);
                removeEdge(b, a, maxEdgeId);
                // Add current edge e instead.
                T[u].push_back({v, e.id});
                T[v].push_back({u, e.id});
        }
    }
}
 
// ----- Upper Bound Computation -----
// Process edges in decreasing order. We use a similar procedure, maintaining a spanning tree T.
// For each edge (in descending order) with endpoints u and v:
//   - If not connected in T, then Rbound = 10^9.
//   - Otherwise, find the unique path and let k be the edge with maximum weight on the path.
//     Set Rbound = (w_i + w_k) / 2.
//     Also, if w_i < w_k, then replace that edge in T by the current edge.
void computeUpperBounds() {
    Rbound.assign(M, 0);
    vector<vector<pair<int,int>>> T(N);
    
    vector<Edge> sorted = edges;
    sort(sorted.begin(), sorted.end(), [](const Edge &a, const Edge &b){
        return a.w > b.w; // descending order.
    });
    
    auto connected = [&](int s, int t) -> bool {
        vector<bool> vis(N, false);
        function<bool(int)> dfs = [&](int cur) -> bool {
            if(cur == t) return true;
            vis[cur] = true;
            for(auto &pr : T[cur]) {
                int nxt = pr.first;
                if(!vis[nxt] && dfs(nxt))
                    return true;
            }
            return false;
        };
        return dfs(s);
    };
    
    for(auto &e : sorted) {
        int u = e.u, v = e.v;
        if(!connected(u, v)) {
            // Not connected: edge is used for very large X.
            Rbound[e.id] = 1000000000LL;
            T[u].push_back({v, e.id});
            T[v].push_back({u, e.id});
        } else {
            vector<int> pathEdgeIds;
            findPath(u, v, -1, T, pathEdgeIds);
            int maxEdgeId = -1;
            ll maxW = -1;
            for (int id : pathEdgeIds) {
                ll wtemp = edges[id].w;
                if(wtemp > maxW) { maxW = wtemp; maxEdgeId = id; }
            }
            Rbound[e.id] = (e.w + maxW) / 2; // note: floor division.
                int a = edges[maxEdgeId].u, b = edges[maxEdgeId].v;
                auto removeEdge = [&](int x, int y, int eid) {
                    for(auto it = T[x].begin(); it != T[x].end(); ++it) {
                        if(it->first == y && it->second == eid) {
                            T[x].erase(it);
                            break;
                        }
                    }
                };
                removeEdge(a, b, maxEdgeId);
                removeEdge(b, a, maxEdgeId);
                T[u].push_back({v, e.id});
                T[v].push_back({u, e.id});
        }
    }
}
 
// ----- Main -----
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> N >> M;
    edges.resize(M);
    for (int i = 0; i < M; i++){
        int u, v; ll w;
        cin >> u >> v >> w;
        u--; v--; // convert to 0-indexed
        edges[i] = {u, v, w, i};
    }
    Lbound.resize(M); Rbound.resize(M);
    computeLowerBounds();
    computeUpperBounds();
 
    // (For debugging, you may print each edge's interval:)
    // for (int i = 0; i < M; i++){
    //     cout << "Edge " << i+1 << " (w=" << edges[i].w << "): [" << Lbound[i] << ", " << Rbound[i] << "]\n";
    // }
 
    // Answer queries.
    // For each query X, the MST chosen is exactly the set of edges i for which X is in [Lbound[i], Rbound[i]].
    // The cost is sum_{i with X in [L_i,R_i]} |w_i - X|.
    int Q; cin >> Q;
    while(Q--){
        ll X; cin >> X;
        ll totalCost = 0;
        for (int i = 0; i < M; i++){
            if(Lbound[i] <= X && X <= Rbound[i]){
                totalCost += (X >= edges[i].w ? (X - edges[i].w) : (edges[i].w - X));
            }
        }
        cout << totalCost << "\n";
    }
    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...