--- tags: Hash, Matrix Hash, Multi Hash, Multi Matrix Hash, DP, Template, Partial Sum, DP --- SPyofgame Matrix Hash Template === [link](link) ----- ###### Tags: Matrix Hash, Multi Matrix Hash, Partial Sum, DP ----- ### Mục tiêu - Phiên bản static offline - Offset $1, 1$ - Ô đầu ở $(1, 1)$ kết thúc $(n, m)$ - Tiền xử lí $init(c[][], n, m)$ trong $O(n \times m)$ thưc hiện xây dựng mảng hash trên ma trận $c[][]$ từ góc trái trên $(1, 1)$ đến góc phải dưới $(n, m)$ - Truy vấn $query(x, y, u, v)$ trong $O(1)$ cho biết giá trị hash của ma trận có góc trái trên $(x, y)$ và góc phải dưới $(u, v)$ ----- ### Hằng số Với $k$ là giá trị lớn nhất trong $c[][]$ - $ROWLIM > n$ là max độ dài hàng. - $COLLIM > m$ là max độ dài cột. - $ROWBASE > k$ là hằng số hash trên hàng. - $COLBASE > k$ là hằng số hash trên cột. - $MODULO$ > 1 là hằng số modulo của mã Hash. Hash đạt hiêu quả nhất khi - $ROWBASE > k + 1$ - $COLBASE > k + 1$ - $ROWBASE \neq COLBASE$ - $MODULO \approx 10^9$ và nguyên tố ----- ### Hướng dẫn Tương tư như Hash trên một xâu, ta sẽ biểu diễn giá trị dưới dạng đa thức - Ta định nghĩa $V[x][y]$ là giá trị hash tại ô $[x][y]$ - Ta có $V[x][y] = c[x][y] \times p^x \times q^y$ Với một bảng: ```css [_][_][_][_] [_][a][b][c] [_][d][e][f] [_][g][h][i] ``` Khi ta hash ta sẽ mong nó ra giá trị như thế này ```css [a * p^0 * q^0][b * p^0 * q^1][c * p^0 * q^2] [d * p^1 * q^0][e * p^1 * q^1][f * p^1 * q^2] [g * p^2 * q^0][h * p^2 * q^1][i * p^2 * q^2] ``` hay :::info $a \times 1 \qquad \ \ \ b \times q \qquad \ \ \ c \times q^2$ $d \times p \qquad \ \ e \times pq \qquad \ f \times pq^2$ $g \times p^2 \qquad h \times p^2q \qquad i \times p^2q^2$ ::: Để tính truy vấn trên một hình chữ nhật bất kì trong $O(1)$ ta sử dụng ý tưởng của mảng cộng dồn. - Ta định nghĩa $H[x][y]$ là giá trị hash của ma trận góc trái trên $[1][1]$ và góc phải dưới $[x][y]$ - Với $x = 0$ ta có $H[0][y] = 0\ \forall\ y \geq 0$ - Với $y = 0$ ta có $H[x][0] = 0\ \forall\ x \geq 0$ - Ngược lại ta có $H[x][y] = \overset{x}{\underset{p = 1}{\Large \Sigma}} \overset{y}{\underset{q = 1}{\Large \Sigma}} V[x][y] = P[x][y] + H[x - 1][y] \times p + H[x][y - 1] \times q - H[x-1][y-1] \times pq$ Lúc này truy vấn một ma trận góc trái trên $(x, y)$ và góc phải dưới $(u, v)$ được tính như sau - $Q = H[u][v] - H[x - 1][v] \times p^{u-x+1} - H[u][y - 1] \times q^{v-y+1} + H[x-1][y-1] \times p^{u-x+1} \times q^{v-y+1}$ ----- ### Hash Code > **Declared Time:** $O(ROWLIM \times COLLIM)$ > **Declared Space:** $O(ROWLIM \times COLLIM)$ > **Precalculation Time:** $O(n \times m)$ > **Precalculation Space:** $O(n \times m)$ > **Query Time:** $O(1)$ > **Query Space:** $O(1)$ > **Accuracy:** Quite High > **Constant:** High > **Algo:** Matrix Hash, Partial Sum, DP ```cpp= template<const int ROWLIM, const int COLLIM, const int ROWBASE, const int COLBASE, const int MODULO> struct Matrix_Hash { /// For convenient const int ROWCOL = (1LL * ROWBASE * COLBASE) % MODULO; int R[ROWLIM]; /// ROWBASE ^ N % MODULO int C[COLLIM]; /// COLBASE ^ M % MODULO int H[ROWLIM][COLLIM]; /// Hashtable /// Initialize this hash void init(const vector<vector<char> > &c, int n, int m) { /// Calculate Rowbase Modulo Power R[0] = 1; for (int i = 1; i < ROWLIM; ++i) R[i] = (1LL * R[i - 1] * ROWBASE) % MODULO; /// Calculate Colbase Modulo Power C[0] = 1; for (int j = 1; j < COLLIM; ++j) C[j] = (1LL * C[j - 1] * COLBASE) % MODULO; /// Calculate 2D Hash for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { H[i][j] = ( c[i][j] + 1LL * H[i - 1][j] * COLBASE + 1LL * H[i][j - 1] * ROWBASE - 1LL * H[i - 1][j - 1] * ROWCOL ) % MODULO; if (H[i][j] < 0) H[i][j] += MODULO; } } } /// Matrix Hash [x, y] -> [u, v] inclusive int query(int x, int y, int u, int v) { int XY = (1LL * C[u - x + 1] * R[v - y + 1]) % MODULO; int res = ( H[u][v] - 1LL * H[x - 1][v] * C[u - x + 1] - 1LL * H[u][y - 1] * R[v - y + 1] + 1LL * H[x - 1][y - 1] * XY ) % MODULO; if (res < 0) res += MODULO; return res; } }; Hash<1010, 1010, 1234, 123456789, 1000000007> H; ``` ----- ### Struct-free Hash Code > **Declared Time:** $O(ROWLIM \times COLLIM)$ > **Declared Space:** $O(ROWLIM \times COLLIM)$ > **Precalculation Time:** $O(n \times m)$ > **Precalculation Space:** $O(n \times m)$ > **Query Time:** $O(1)$ > **Query Space:** $O(1)$ > **Accuracy:** Quite High > **Constant:** Quite High > **Algo:** Matrix Hash, Partial Sum, DP ```cpp char c[ROWLIM][COLLIM]; int R[ROWLIM]; /// ROWBASE ^ N % MODULO int C[COLLIM]; /// COLBASE ^ M % MODULO int H[ROWLIM][COLLIM]; /// Hashtable /// Initialize this hash void init(int n, int m) { /// Calculate Rowbase Modulo Power R[0] = 1; for (int i = 1; i < LIM; ++i) R[i] = (1LL * R[i - 1] * ROWBASE) % MODULO; /// Calculate Colbase Modulo Power C[0] = 1; for (int j = 1; j < LIM; ++j) C[j] = (1LL * C[j - 1] * COLBASE) % MODULO; /// Calculate 2D Hash for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { H[i][j] = ( c[i][j] + 1LL * H[i - 1][j] * COLBASE + 1LL * H[i][j - 1] * ROWBASE - 1LL * H[i - 1][j - 1] * ROWCOL ) % MODULO; if (H[i][j] < 0) H[i][j] += MODULO; } } } /// 2D Hash [x, y] -> [u, v] int query(int x, int y, int u, int v) { int XY = (1LL * C[u - x + 1] * R[v - y + 1]) % MODULO; int res = ( H[u][v] - 1LL * H[x - 1][v] * C[u - x + 1] - 1LL * H[u][y - 1] * R[v - y + 1] + 1LL * H[x - 1][y - 1] * XY ) % MODULO; if (res < 0) res += MODULO; return res; } ``` ----- ### Multi Hash Code > For $K =$ number of hashes > **Declared Time:** $O(ROWLIM \times COLLIM)$ > **Declared Space:** $O(ROWLIM \times COLLIM)$ > **Precalculation Time:** $O(n \times m)$ > **Precalculation Space:** $O(n \times m)$ > **Query Time:** $O(1)$ > **Query Space:** $O(1)$ > **Accuracy:** Significantly higher for larger $K$ > **Constant:** Very High $c_0 = K \times c$ > **Algo:** Matrix Hash, Partial Sum, DP ```cpp= template<const int ROWLIM, const int COLLIM, const int ROWBASE, const int COLBASE, const int MODULO> struct Matrix_Hash { /// For convenient const int ROWCOL = (1LL * ROWBASE * COLBASE) % MODULO; int R[ROWLIM]; /// ROWBASE ^ N % MODULO int C[COLLIM]; /// COLBASE ^ M % MODULO int H[ROWLIM][COLLIM]; /// Hashtable /// Initialize this hash void init(const vector<vector<char> > &c, int n, int m) { /// Calculate Rowbase Modulo Power R[0] = 1; for (int i = 1; i < ROWLIM; ++i) R[i] = (1LL * R[i - 1] * ROWBASE) % MODULO; /// Calculate Colbase Modulo Power C[0] = 1; for (int j = 1; j < COLLIM; ++j) C[j] = (1LL * C[j - 1] * COLBASE) % MODULO; /// Calculate 2D Hash for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { H[i][j] = ( c[i][j] + 1LL * H[i - 1][j] * COLBASE + 1LL * H[i][j - 1] * ROWBASE - 1LL * H[i - 1][j - 1] * ROWCOL ) % MODULO; if (H[i][j] < 0) H[i][j] += MODULO; } } } /// Matrix Hash [x, y] -> [u, v] inclusive int query(int x, int y, int u, int v) { int XY = (1LL * C[u - x + 1] * R[v - y + 1]) % MODULO; int res = ( H[u][v] - 1LL * H[x - 1][v] * C[u - x + 1] - 1LL * H[u][y - 1] * R[v - y + 1] + 1LL * H[x - 1][y - 1] * XY ) % MODULO; if (res < 0) res += MODULO; return res; } }; /// Hexa-Hashes Value typedef tuple<int, int, int, int, int, int> Pack; struct Multi_Matrix_Hash { Matrix_Hash<LIM, LIM, 1234, 123456789, 1000000007> A; Matrix_Hash<LIM, LIM, 12345, 12345678, 1000000009> B; Matrix_Hash<LIM, LIM, 123456, 1234567, 1000000021> C; Matrix_Hash<LIM, LIM, 1234567, 123456, 1000000033> D; Matrix_Hash<LIM, LIM, 12345678, 12345, 1000000087> E; Matrix_Hash<LIM, LIM, 123456789, 1234, 1000000093> F; /// Initialize these hashes void init(const vector<vector<char> > &c, int n, int m) { A.init(c, n, m); B.init(c, n, m); C.init(c, n, m); D.init(c, n, m); E.init(c, n, m); F.init(c, n, m); } /// Multi Matrix Hash [x, y] -> [u, v] inclusive Pack query(int x, int y, int u, int v) { return Pack { A.query(x, y, u, v), B.query(x, y, u, v), C.query(x, y, u, v), D.query(x, y, u, v), E.query(x, y, u, v), F.query(x, y, u, v) }; } }; ```