#include <bits/stdc++.h>
using namespace std;
// DSU for grouping vertices that must have the same color – that is, forming a 1–comp.
struct DSU {
vector<int> par;
DSU(int n) : par(n) {
for (int i = 0; i < n; i++) par[i] = i;
}
int find(int a) {
return par[a] == a ? a : par[a] = find(par[a]);
}
void unite(int a, int b) {
a = find(a); b = find(b);
if(a != b) par[b] = a;
}
};
// Global variables (using 0-indexing)
int N;
vector<vector<int>> mat;
// Each group represents a maximal 1–comp (i.e. a set of vertices that are all connected by f(u,v)=1)
// After contraction, every two vertices from different groups have f(u,v) ≥ 2.
struct Group {
int rep; // one chosen representative vertex
vector<int> verts; // all vertices in the 1–comp
int level; // the “level” = f(root, rep) (should equal the number of distinct colors on the path from the root)
int col; // color assigned to this group (all its vertices get this color)
int parent; // parent group in the contracted tree (–1 for the root group)
vector<int> children; // children groups
};
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int subtask;
cin >> subtask >> N;
mat.assign(N, vector<int>(N, 0));
for (int i = 0; i < N; i++){
for (int j = 0; j < N; j++){
cin >> mat[i][j];
}
}
// 1. Collapse vertices that must be the same color (i.e. f(u,v)=1) into 1–comps.
DSU dsu(N);
for (int i = 0; i < N; i++){
for (int j = i+1; j < N; j++){
if(mat[i][j] == 1)
dsu.unite(i, j);
}
}
// 2. Build the list of groups (1–comps). By the problem’s consistency,
// all vertices in one 1–comp have identical rows and must get the same color.
map<int, int> groupId; // maps DSU root -> group id
vector<Group> groups;
for (int i = 0; i < N; i++){
int r = dsu.find(i);
if(groupId.find(r) == groupId.end()){
groupId[r] = groups.size();
Group g;
g.rep = i; // choose the first encountered vertex as representative
g.verts.push_back(i);
g.parent = -1;
g.level = 0;
g.col = 0;
groups.push_back(g);
} else {
groups[groupId[r]].verts.push_back(i);
}
}
int M = groups.size();
// 3. Choose the 1–comp that contains vertex 0 as the root.
int rootGroup = groupId[ dsu.find(0) ];
groups[rootGroup].level = 1; // by definition, the path from the root to itself has 1 distinct color.
// For every other group, define its level as mat[0][rep].
for (int i = 0; i < M; i++){
if(i == rootGroup) continue;
groups[i].level = mat[0][ groups[i].rep ];
}
// 4. Connect the groups. In a valid tree, when moving from a vertex to its neighbor
// (from a group to another), the number of distinct colors increases exactly by one.
// So for every group (other than the root), we choose as its parent any group U
// with level = (child’s level - 1) and such that f(U.rep, child.rep) = 2.
for (int i = 0; i < M; i++){
if(i == rootGroup) continue;
int targetLevel = groups[i].level - 1;
int parCandidate = -1;
for (int j = 0; j < M; j++){
if(j == i) continue;
if(groups[j].level == targetLevel && mat[ groups[j].rep ][ groups[i].rep ] == 2) {
parCandidate = j;
break;
}
}
// In a correct instance such a candidate always exists.
groups[i].parent = parCandidate;
groups[parCandidate].children.push_back(i);
}
// 5. Now assign colors to groups by doing a DFS on the contracted (group) tree.
// The idea: along the path from the root, the number of distinct colors must equal the level.
// When moving from a group to a child, if the child's level equals (current distinct count + 1),
// then we must pick a new color (one not already in the current path). Otherwise, if the level
// does not increase, reuse the parent’s color.
vector<int> curPath; // stores the colors on the current root-to-node path
function<void(int)> dfs = [&](int u) {
// For the root group we already assign an arbitrary color.
if(u == rootGroup && curPath.empty()){
groups[u].col = 1;
curPath.push_back(1);
}
for (int v : groups[u].children) {
if(groups[v].level == (int)curPath.size() + 1) {
// Must introduce a new color. Choose the smallest color in {1,...,N} not in curPath.
vector<bool> used(N+1, false);
for (int c : curPath) used[c] = true;
int newcol = 1;
while(newcol <= N && used[newcol]) newcol++;
groups[v].col = newcol;
curPath.push_back(newcol);
dfs(v);
curPath.pop_back();
} else if(groups[v].level == (int)curPath.size()){
// Level remains the same: assign the same color as the parent.
groups[v].col = groups[u].col;
curPath.push_back(groups[u].col);
dfs(v);
curPath.pop_back();
} else {
// For a correct instance, this case should not occur.
groups[v].col = 1;
curPath.push_back(1);
dfs(v);
curPath.pop_back();
}
}
};
dfs(rootGroup);
// 6. “Expand” the groups to form the final tree.
// Inside each 1–comp we connect the vertices arbitrarily (here, in a chain),
// and then connect each group to its parent by linking the representative vertices.
vector<pair<int,int>> ansEdges;
// Connect vertices inside the same 1–comp:
for (int i = 0; i < M; i++){
auto &g = groups[i];
if(g.verts.size() > 1){
sort(g.verts.begin(), g.verts.end());
for (size_t j = 1; j < g.verts.size(); j++){
ansEdges.push_back({ g.verts[j-1] + 1, g.verts[j] + 1 });
}
}
}
// Connect different groups (the spanning tree on 1–comps):
for (int i = 0; i < M; i++){
if(i == rootGroup) continue;
int par = groups[i].parent;
ansEdges.push_back({ groups[i].rep + 1, groups[par].rep + 1 });
}
// 7. Now assign each vertex the color of its group.
vector<int> ansColor(N, 0);
for (int i = 0; i < N; i++){
int gr = groupId[ dsu.find(i) ];
ansColor[i] = groups[gr].col;
}
// Output the vertex colors.
for (int i = 0; i < N; i++){
cout << ansColor[i] << (i == N-1 ? "\n" : " ");
}
// Output the tree edges.
for (auto &e : ansEdges){
cout << e.first << " " << e.second << "\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... |