#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 time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |