G. GCD on a grid

G. GCD on a grid

Not long ago, Egor learned about the Euclidean algorithm for finding the greatest common divisor of two numbers. The greatest common divisor of two numbers a and b is the largest number that divides both a and b without leaving a remainder. With this knowledge, Egor can solve a problem that he once couldn't.

Vasily has a grid with n rows and m columns, and the integer aij is located at the intersection of the i-th row and the j-th column. Egor wants to go from the top left corner (at the intersection of the first row and the first column) to the bottom right corner (at the intersection of the last row and the last column) and find the greatest common divisor of all the numbers along the path. He is only allowed to move down and to the right. Egor has written down several paths and obtained different GCD values. He became interested in finding the maximum possible GCD.

Unfortunately, Egor is tired of calculating GCDs, so he asks for your help in finding the maximum GCD of the integers along the path from the top left corner to the bottom right corner of the grid.

Input

The first line contains an integer t (1t104) — the number of test cases.

The first line of each test case contains two integers n and m (1n,m100) — the number of rows and columns of the grid.

Then, there are n lines, where the i-th line contains m integers (1ai,j106) — the integers written in the i-th row and the j-th column of the grid.

It is guaranteed that the sum of nm does not exceed 2105 over all test cases.

Output

For each test case, output the maximum possible GCD along the path from the top left cell to the bottom right cell in a separate line.

Example

input

3
2 3
30 20 30
15 25 40
3 3
12 4 9
3 12 2
8 3 12
2 4
2 4 6 8
1 3 6 9

output

10
3
1

 

解题思路

  由于要从 (0,0) 走到 (n1,m1),因此 (0,0)(n1,m1) 所有路径的最大公约数只可能是 g0,0gn1,m1 的公约数,即 gcd(g0,0,gn1,m1) 的约数。为此我们先对 gcd(g0,0,gn1,m1) 分解约数存储到数组 p 中,并用 pk 来表示第 k 个约数,哈希表 mp[x] 表示 x 是第几个约数。

  定义 f(i,j,k) 表示是否存在 (0,0)(i,j) 最大公约数为 pk 的路径。那么 f(i,j,k) 可以转移到的状态有 f(i+1,j,mp[gcd(gi+1,j,pk)])f(i,j+1,mp[gcd(gi,j+1,pk)])

  最后就是枚举 k 判断是否存在一个状态 f(n1,m1,k)true 即可。

  可以发现 k 的大小就是一个数约数的个数。在 1106 中,一个数最多有 240 个约数。另外当 A 不是特别大时,约数个数的估计公式为 A3

  AC 代码如下,时间复杂度为 O(nmA3logA)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 105, M = 245, K = 1e6 + 5;

int g[N][N];
int p[M], mp[K], sz;
bool f[N][N][M];

void solve() {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> g[i][j];
        }
    }
    sz = 0;
    int d = __gcd(g[0][0], g[n - 1][m - 1]);
    for (int i = 1; i * i <= d; i++) {
        if (d % i == 0) {
            p[sz++] = i;
            if (d / i != i) p[sz++] = d / i;
        }
    }
    for (int i = 0; i < sz; i++) {
        mp[p[i]] = i;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < sz; k++) {
                f[i][j][k] = false;
            }
        }
    }
    f[0][0][mp[d]] = true;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < sz; k++) {
                if (!f[i][j][k]) continue;
                if (i + 1 < n) f[i + 1][j][mp[__gcd(g[i + 1][j], p[k])]] = true;
                if (j + 1 < m) f[i][j + 1][mp[__gcd(g[i][j + 1], p[k])]] = true;
            }
        }
    }
    int ret = 0;
    for (int i = 0; i < sz; i++) {
        if (f[n - 1][m - 1][i]) ret = max(ret, p[i]);
    }
    cout << ret << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

  再提供另外一个做法。

  枚举 gcd(g0,0,gn1,m1) 的每一个约数 d,把满足 dgi,j 的位置 (i,j) 标记出来,判断能否只通过这些被标记的位置从 (0,0) 走到 (n1,m1),可以用 dp 实现,与上面的实现类似。

  AC 代码如下,时间复杂度为 O(nmA3)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 105;

int n, m;
int g[N][N];
bool vis[N][N];
bool f[N][N];

bool check(int d) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (g[i][j] % d == 0) vis[i][j] = true;
            else vis[i][j] = false;
            f[i][j] = false;
        }
    }
    f[0][0] = true;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (!f[i][j]) continue;
            if (vis[i + 1][j]) f[i + 1][j] = true;
            if (vis[i][j + 1]) f[i][j + 1] = true;
        }
    }
    return f[n - 1][m - 1];
}

void solve() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> g[i][j];
        }
    }
    int d = __gcd(g[0][0], g[n - 1][m - 1]);
    int ret = 0;
    for (int i = 1; i * i <= d; i++) {
        if (d % i == 0) {
            if (check(i)) ret = max(ret, i);
            if (d / i != i && check(d / i)) ret = max(ret, d / i);
        }
    }
    cout << ret << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Codeforces Round 938 (Div. 3) Editorial:https://codeforces.com/blog/entry/128243

posted @   onlyblues  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-04-13 查询字符串
2021-04-13 Saving James Bond - Hard Version
Web Analytics
点击右上角即可分享
微信分享提示