Submission #1143654

#TimeUsernameProblemLanguageResultExecution timeMemory
1143654RufatRoad Service 2 (JOI24_ho_t5)C++20
0 / 100
0 ms320 KiB
#include <bits/stdc++.h>
using namespace std;
 
// Disjoint Set Union (DSU) structure for union-find operations.
struct DSU {
    vector<int> parent;
    vector<int> rank;
    DSU(int n) {
        parent.resize(n);
        rank.assign(n, 0);
        for(int i = 0; i < n; i++) parent[i] = i;
    }
    int find(int a) {
        if(parent[a] == a)return a;
        return parent[a] = find(parent[a]);
    }
    // merge returns true if a merge happened.
    bool merge(int a, int b) {
        a = find(a), b = find(b);
        if(a == b)return false;
        if(rank[a] < rank[b]) swap(a,b);
        parent[b] = a;
        if(rank[a] == rank[b]) rank[a]++;
        return true;
    }
};
 
// Global variables for grid dimensions.
int H, W, Q;
 
// We will store the status of horizontal segments (A) and vertical segments (B).
// A[i] is a string of length (W-1) for row i (0-indexed) – segment between (i,j) and (i,j+1)
// B[i] is a string of length (W) for the vertical segments between row i and i+1.
vector<string> A; // size H.
vector<string> B; // size H-1.
 
// Repair cost for each east-west road (each row), 0-indexed.
vector<int> Ccost;
 
// We number intersections as: index = i * W + j, with i in [0,H-1], j in [0,W-1].
int idx(int i, int j){
    return i * W + j;
}
 
// --- MAIN ---
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> H >> W >> Q;
    A.resize(H);
    for (int i = 0; i < H; i++){
        cin >> A[i];
    }
    B.resize(H-1);
    for (int i = 0; i < H-1; i++){
        cin >> B[i];
    }
    Ccost.resize(H);
    for (int i = 0; i < H; i++){
        cin >> Ccost[i];
    }
 
    int nInter = H * W;
    // (1) Compute the base connectivity (the grid’s DSU) using originally passable roads.
    DSU baseDSU(nInter);
    // Horizontal edges (only if A[i][j]=='1')
    for (int i = 0; i < H; i++){
        for (int j = 0; j < W-1; j++){
            if(A[i][j]=='1'){
                int u = idx(i,j), v = idx(i,j+1);
                baseDSU.merge(u,v);
            }
        }
    }
    // Vertical edges (only if B[i][j]=='1')
    for (int i = 0; i < H-1; i++){
        for (int j = 0; j < W; j++){
            if(B[i][j]=='1'){
                int u = idx(i,j), v = idx(i+1,j);
                baseDSU.merge(u,v);
            }
        }
    }
    // Compute each intersection’s base component id.
    vector<int> comp(nInter);
    for (int i = 0; i < nInter; i++){
        comp[i] = baseDSU.find(i);
    }
 
    // (2) For each row, record the (distinct) base component ids that appear.
    vector<vector<int>> rowComps(H);
    for (int i = 0; i < H; i++){
        int prev = -1;
        for (int j = 0; j < W; j++){
            int id = comp[idx(i,j)];
            if(j == 0 || id != prev){
                rowComps[i].push_back(id);
                prev = id;
            }
        }
        sort(rowComps[i].begin(), rowComps[i].end());
        rowComps[i].erase(unique(rowComps[i].begin(), rowComps[i].end()), rowComps[i].end());
    }
 
    // (3) For each base component id, record in which rows it appears.
    // Note: base component ids range up to nInter.
    vector<vector<int>> compRows(nInter);
    for (int i = 0; i < H; i++){
        for (int cid : rowComps[i]){
            compRows[cid].push_back(i);
        }
    }
    for (int i = 0; i < nInter; i++){
        if(!compRows[i].empty()){
            sort(compRows[i].begin(), compRows[i].end());
            compRows[i].erase(unique(compRows[i].begin(), compRows[i].end()), compRows[i].end());
        }
    }
 
    // (4) Answer queries.
    // For each query we:
    //   – Read T required intersections.
    //   – “Compress” them to their base component ids (i.e. reqComps).
    //   – If there is only one base component then output 0.
    //   – Otherwise, we “gather” candidate rows (i.e. rows that appear for at least one required base component)
    //     and for each such row, compute the intersection with reqComps.
    //   – Then (greedily) process candidate rows (viewed as “hyperedges”) in increasing order of repair cost;
    //     if a candidate row connects two or more “groups” (of required base components) that are not yet merged,
    //     we “take” that repair (add its cost once) and merge the groups.
    //   – Finally, if all required base components are merged into one group, we output the total cost; otherwise –1.
    //
    // (We note that the general problem is NP–hard; many contestants use such a greedy–hyperedge–DSU approach for partial/full score.)
    for (int qi = 0; qi < Q; qi++){
        int T;
        cin >> T;
        vector<pair<int,int>> reqPoints(T);
        for (int i = 0; i < T; i++){
            int x, y;
            cin >> x >> y;
            reqPoints[i] = {x-1, y-1}; // convert to 0-indexed
        }
 
        // Build the set of required base components.
        vector<int> reqComps;
        {
            unordered_set<int> s;
            for (int i = 0; i < T; i++){
                int r = reqPoints[i].first, c = reqPoints[i].second;
                int compId = comp[idx(r,c)];
                s.insert(compId);
            }
            reqComps.assign(s.begin(), s.end());
        }
        sort(reqComps.begin(), reqComps.end());
 
        if(reqComps.size() == 1){
            cout << 0 << "\n";
            continue;
        }
 
        // Map each required base component id to a compressed index.
        unordered_map<int,int> compToIndex;
        for (int i = 0; i < (int)reqComps.size(); i++){
            compToIndex[reqComps[i]] = i;
        }
        int k = reqComps.size();
 
        // (a) Gather candidate rows. For each required base component,
        // add all rows (from compRows) in which it appears.
        unordered_set<int> candRowsSet;
        for (int compId : reqComps) {
            for (int r : compRows[compId]) {
                candRowsSet.insert(r);
            }
        }
        vector<int> candRows;
        candRows.assign(candRowsSet.begin(), candRowsSet.end());
        // Sort candidate rows by repair cost (if equal, by row index)
        sort(candRows.begin(), candRows.end(), [&](int r1, int r2){
            if(Ccost[r1] == Ccost[r2]) return r1 < r2;
            return Ccost[r1] < Ccost[r2];
        });
 
        // (b) For each candidate row, compute the intersection between rowComps[r] and reqComps.
        // We only need candidate rows that “cover” at least 2 required base components.
        struct Candidate {
            int row;
            int cost;
            vector<int> comps; // these will be the indices (0 to k-1) corresponding to reqComps
        };
        vector<Candidate> candidates;
        for (int r : candRows){
            vector<int> inter;
            int i = 0, j = 0;
            while(i < (int)rowComps[r].size() && j < (int)reqComps.size()){
                if(rowComps[r][i] == reqComps[j]){
                    inter.push_back(compToIndex[rowComps[r][i]]);
                    i++; j++;
                } else if(rowComps[r][i] < reqComps[j]){
                    i++;
                } else {
                    j++;
                }
            }
            if(inter.size() >= 2){
                Candidate cand;
                cand.row = r;
                cand.cost = Ccost[r];
                sort(inter.begin(), inter.end());
                inter.erase(unique(inter.begin(), inter.end()), inter.end());
                cand.comps = move(inter);
                candidates.push_back(cand);
            }
        }
 
        // (c) Process the candidate rows (hyperedges) in increasing order of cost.
        sort(candidates.begin(), candidates.end(), [](const Candidate &a, const Candidate &b){
            if(a.cost == b.cost) return a.row < b.row;
            return a.cost < b.cost;
        });
 
        // DSU for the query (on the k required base components).
        DSU queryDSU(k);
        int usedCost = 0;
        for (auto &cand : candidates) {
            // Determine the DSU groups among the required components in this candidate row.
            unordered_set<int> groups;
            for (int compIdx : cand.comps) {
                groups.insert(queryDSU.find(compIdx));
            }
            if(groups.size() <= 1) continue;
            // Use this candidate row (i.e. repair that row) to merge all groups.
            int rep = *groups.begin();
            bool mergedSomething = false;
            for (int compIdx : cand.comps) {
                if(queryDSU.merge(rep, compIdx)){
                    mergedSomething = true;
                }
            }
            if(mergedSomething){
                usedCost += cand.cost;
            }
            // Check if all required nodes are now connected.
            int root0 = queryDSU.find(0);
            bool allConnected = true;
            for (int i = 1; i < k; i++){
                if(queryDSU.find(i) != root0) { allConnected = false; break; }
            }
            if(allConnected) break;
        }
 
        int root0 = queryDSU.find(0);
        bool allConnected = true;
        for (int i = 1; i < k; i++){
            if(queryDSU.find(i) != root0) { allConnected = false; break; }
        }
        if(allConnected) cout << usedCost << "\n";
        else cout << -1 << "\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...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...