#include <bits/stdc++.h>
using namespace std;
 
// DSU for grouping vertices that must have the same colour (their rows are identical)
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 (we use 0-indexing)
int N;
vector<vector<int>> mat;
 
// For the group–tree we store for each group an id, a representative vertex, and a list of member vertices.
struct Group {
    int rep;
    vector<int> verts;
    // f = "distance from the chosen root" (the number from mat[rep_root][rep])
    int f;
    int col; // colour assigned to this group
    int parent; // parent group id in the group tree (-1 for root)
    vector<int> children;
};
 
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. DSU – group together vertices that “must” have the same colour.
    DSU dsu(N);
    for (int i=0; i<N; i++){
        for (int j=i+1; j<N; j++){
            // In any valid solution the matrix entry is 1 exactly when the entire path is monochromatic.
            if(mat[i][j]==1){
                dsu.unite(i,j);
            }
        }
    }
 
    // 2. Build groups. Two vertices in the same group have identical rows.
    map<int, int> groupId; // dsu-root -> new 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; // take first vertex encountered as representative
            g.verts.push_back(i);
            g.parent = -1;
            g.f = 0;
            g.col = 0;
            groups.push_back(g);
        } else {
            groups[groupId[r]].verts.push_back(i);
        }
    }
    int M = groups.size();
 
    // 3. Choose the group that contains vertex 0 as the "root".
    int rootGroup = groupId[ dsu.find(0) ];
    groups[rootGroup].f = 1; // by definition
    // For every other group, we will “set” f = mat[0][rep] (using the fact that in any valid solution,
    // if you take the vertex from the root–group as reference, the number of distinct colours on the path
    // from the root to a vertex v equals mat[0][v]).
    for (int i=0; i<M; i++){
        if(i==rootGroup) continue;
        groups[i].f = mat[0][ groups[i].rep ];
    }
 
    // 4. For every group (other than the root) we pick as its parent any group U 
    // with f = f(child) - 1 such that mat[ U.rep ][ child.rep ] == 2.
    // (It is not hard to show that such a U exists.)
    for (int i=0; i<M; i++){
        if(i==rootGroup) continue;
        int ftarget = groups[i].f - 1;
        int parCandidate = -1;
        for (int j=0; j<M; j++){
            if(j==i) continue;
            if(groups[j].f == ftarget && mat[ groups[j].rep ][ groups[i].rep ] == 2) {
                parCandidate = j; break;
            }
        }
        // (In a correct instance, parCandidate will be found.)
        groups[i].parent = parCandidate;
        groups[parCandidate].children.push_back(i);
    }
 
    // 5. Now assign colours to groups by doing a DFS on the group–tree.
    // The idea is: on the unique path from the root group to any group G, the number of distinct colours must equal groups[G].f.
    // So if f(child)==f(parent) then we force the same colour;
    // if f(child)==f(parent)+1 then we assign a colour new (i.e. not used on the path).
    vector<int> groupColor(M, 0);
    vector<int> curPath; // colours used on the current path
    function<void(int)> dfs = [&](int u) {
        // If u is the root, assign colour 1 arbitrarily.
        if(u==rootGroup) {
            groups[u].col = 1;
            curPath.push_back(1);
        }
        for (int v : groups[u].children) {
            // The desired distinct count on the path from the root to group v is groups[v].f.
            // The current distinct count on the path (from root to u) is (int)curPath.size().
            if(groups[v].f == (int)curPath.size() + 1){
                // must choose a new colour – choose the smallest colour from 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].f == (int)curPath.size()){
                // do not introduce a new colour: use parent's colour.
                groups[v].col = groups[u].col;
                curPath.push_back(groups[u].col);
                dfs(v);
                curPath.pop_back();
            } else {
                // If the number does not match, the instance is not “nice” – but the problem guarantees a solution.
                // (We could also try backtracking here.)
                groups[v].col = 1;
                curPath.push_back(1);
                dfs(v);
                curPath.pop_back();
            }
        }
    };
    dfs(rootGroup);
 
    // 6. Now “expand” the groups to form the final tree.
    // For each group, we join all vertices in that group arbitrarily (for example, in a chain).
    // Then, for every group (except the root) we add an edge connecting one vertex of the group
    // (say, its representative) to one vertex of its parent group.
    vector<pair<int,int>> ansEdges;
    // Within groups:
    for (int i=0; i<M; i++){
        auto &g = groups[i];
        if(g.verts.size() > 1){
            sort(g.verts.begin(), g.verts.end());
            for (int j=1; j<(int)g.verts.size(); j++){
                // use 1-indexed vertices in output
                ansEdges.push_back({ g.verts[j-1] + 1, g.verts[j] + 1 });
            }
        }
    }
    // Between groups (the spanning tree on groups):
    for (int i=0; i<M; i++){
        if(i==rootGroup) continue;
        int par = groups[i].parent;
        // connect group i and group par by taking (for example) the representative of each.
        ansEdges.push_back({ groups[i].rep + 1, groups[par].rep + 1 });
    }
 
    // 7. Now output a colouring for the N vertices.
    // Each vertex gets the colour 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:
    for (int i=0; i<N; i++){
        cout << ansColor[i] << (i==N-1? "\n" : " ");
    }
    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... |