Challenge: Coloring Grid

Subdomeniu: Combinatorics (combinatorics)

Scor cont: 80.0 / 80

Submission status: Accepted

Submission score: 1.0

Submission ID: 464791811

Limbaj: cpp14

Link challenge: https://www.hackerrank.com/challenges/coloring-grid/problem

Cerinta

Calculate the number of ways to color an N * M grid using K colors. Adjacent squares in the grid should have different colors. Squares are considered adjacent if they share an edge.

**Input Format**  
The first line contains an integer T denoting the number of test-cases.
The next T lines contains integers N, M and K separated by a single space. 

**Output Format**  
Output T lines, one for each test case containing the number of ways modulo 10<sup>9</sup>+7. 

**Constraints**   
1 <= T <= 10<sup>5</sup>    
1 <= N,M <= 8  
1 <= K <= 10<sup>9</sup>    

**Sample Input**

    3
    3 3 2
    3 4 3
    1 1 1

**Sample Output** 

    2
    1122
    1

**Explanation**  
For the first case, there are two ways to color the grid. The colorings are in a chessboard pattern with either color at the top right square.

**Timelimits**
Timelimits for this challenge can be seen [here](https://www.hackerrank.com/environment)

Cod sursa

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <cstring>

using namespace std;

const long long MOD = 1000000007;

// W[N][M][C] represents the number of valid colorings of an N x M grid exactly using C unlabelled colors.
long long W[9][9][65]; 

map<vector<int>, int> prof_to_id;
vector<vector<int>> id_to_prof;

// Normalizes a coloring sequence to abstract its equivalence relation.
int get_id(const vector<int>& p) {
    vector<int> norm(p.size());
    int m[20];
    for (int i = 0; i < 20; ++i) m[i] = -1;
    int nxt = 0;
    for (int i = 0; i < (int)p.size(); ++i) {
        if (m[p[i]] == -1) m[p[i]] = nxt++;
        norm[i] = m[p[i]];
    }
    auto it = prof_to_id.find(norm);
    if (it != prof_to_id.end()) return it->second;
    int id = (int)prof_to_id.size();
    prof_to_id[norm] = id;
    id_to_prof.push_back(norm);
    return id;
}

// Caches to avoid redundant expensive normalizations 
int trans_in[5005][10][2];
int trans_out[5005][2];

// Precomputes values iteratively using dynamic programming
void precompute() {
    for (int M = 1; M <= 8; ++M) {
        prof_to_id.clear();
        id_to_prof.clear();
        memset(trans_in, -1, sizeof(trans_in));
        memset(trans_out, -1, sizeof(trans_out));
        
        vector<vector<long long>> dp;
        int dummy_id = get_id({});
        (void)dummy_id;
        dp.resize(1);
        dp[0].resize(65, 0);
        dp[0][0] = 1;
        
        // Given N <= M symmetry, we can just process cells up to M * M.
        for (int i = 0; i < M * M; ++i) {
            int r = i / M;
            int c = i % M;
            int r_gt_0 = (r > 0 ? 1 : 0);
            
            vector<vector<long long>> next_dp(id_to_prof.size(), vector<long long>(65, 0));
            
            for (int id = 0; id < (int)dp.size(); ++id) {
                // Pruning empty tracks
                bool has_any = false;
                for (int C = 0; C <= i; ++C) {
                    if (dp[id][C]) { has_any = true; break; }
                }
                if (!has_any) continue;
                
                vector<int> prof = id_to_prof[id];
                int c_prof = 0;
                for (int x : prof) c_prof = max(c_prof, x);
                if (!prof.empty()) c_prof += 1;
                
                int forbidden_above = (r > 0) ? prof[0] : -1;
                int forbidden_left = (c > 0) ? prof.back() : -1;
                
                // Option 1: Pick a valid matching color from current profile window
                for (int x = 0; x < c_prof; ++x) {
                    if (x != forbidden_above && x != forbidden_left) {
                        int nid = trans_in[id][x][r_gt_0];
                        if (nid == -1) {
                            vector<int> nprof = prof;
                            if (r > 0) nprof.erase(nprof.begin());
                            nprof.push_back(x);
                            nid = get_id(nprof);
                            trans_in[id][x][r_gt_0] = nid;
                        }
                        if (nid >= (int)next_dp.size()) next_dp.resize(nid + 1, vector<long long>(65, 0));
                        for (int C = 0; C <= i; ++C) {
                            if (long long val = dp[id][C]) {
                                next_dp[nid][C] = (next_dp[nid][C] + val) % MOD;
                            }
                        }
                    }
                }
                
                // Cache ID generation for "off-profile" colors
                int nid = trans_out[id][r_gt_0];
                if (nid == -1) {
                    vector<int> nprof = prof;
                    if (r > 0) nprof.erase(nprof.begin());
                    nprof.push_back(c_prof);
                    nid = get_id(nprof);
                    trans_out[id][r_gt_0] = nid;
                }
                if (nid >= (int)next_dp.size()) next_dp.resize(nid + 1, vector<long long>(65, 0));
                
                // Option 2 & 3: Pick a previously unused color, or a color missing from current window
                for (int C = 0; C <= i; ++C) {
                    if (long long val = dp[id][C]) {
                        if (C > c_prof) {
                            long long ways = C - c_prof;
                            next_dp[nid][C] = (next_dp[nid][C] + val * ways) % MOD;
                        }
                        if (C + 1 <= 64) {
                            next_dp[nid][C + 1] = (next_dp[nid][C + 1] + val) % MOD;
                        }
                    }
                }
            }
            dp = move(next_dp);
            
            // Record configurations tracking the fully completed NxM grid blocks 
            if (c == M - 1) {
                int N = r + 1;
                for (int id = 0; id < (int)dp.size(); ++id) {
                    for (int C = 1; C <= N * M; ++C) {
                        W[N][M][C] = (W[N][M][C] + dp[id][C]) % MOD;
                    }
                }
            }
        }
    }
}

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    precompute();
    
    int T;
    if (!(cin >> T)) return 0;
    
    while (T--) {
        int N, M;
        long long K;
        cin >> N >> M >> K;
        
        // Ensure bounds scale up to max tracked M
        if (N > M) swap(N, M);
        
        long long ans = 0;
        long long P = 1;
        
        for (int C = 1; C <= min((long long)N * M, K); ++C) {
            P = (P * ((K - C + 1) % MOD)) % MOD;
            long long term = (W[N][M][C] * P) % MOD;
            ans = (ans + term) % MOD;
        }
        cout << ans << "\n";
    }
    return 0;
}
HackerRank Combinatorics – Coloring Grid