首页 > 其他分享 >G. GCD on a grid

G. GCD on a grid

时间:2024-04-13 17:44:06浏览次数:20  
标签:约数 GCD int ++ grid gcd

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 ${a_i}_j$ 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$ ($1 \le t \le {10}^{4}$) — the number of test cases.

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

Then, there are $n$ lines, where the $i$-th line contains $m$ integers $(1 \le a_{i,j} \le {10}^{6}$) — the integers written in the $i$-th row and the $j$-th column of the grid.

It is guaranteed that the sum of $n \cdot m$ does not exceed $2 \cdot {10}^{5}$ 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)$ 走到 $(n-1,m-1)$,因此 $(0,0) \to (n-1,m-1)$ 所有路径的最大公约数只可能是 $g_{0,0}$ 和 $g_{n-1,m-1}$ 的公约数,即 $\gcd(g_{0,0}, g_{n-1,m-1})$ 的约数。为此我们先对 $\gcd(g_{0,0}, g_{n-1,m-1})$ 分解约数存储到数组 $p$ 中,并用 $p_k$ 来表示第 $k$ 个约数,哈希表 $\text{mp}[x]$ 表示 $x$ 是第几个约数。

  定义 $f(i,j,k)$ 表示是否存在 $(0,0) \to (i,j)$ 最大公约数为 $p_k$ 的路径。那么 $f(i,j,k)$ 可以转移到的状态有 $f(i+1, j, \text{mp}\left[ \gcd(g_{i+1,j}, \, p_k)\right])$ 和 $f(i, j+1, \text{mp}\left[ \gcd(g_{i,j+1}, \, p_k)\right])$。

  最后就是枚举 $k$ 判断是否存在一个状态 $f(n-1,m-1,k)$ 为 $\text{true}$ 即可。

  可以发现 $k$ 的大小就是一个数约数的个数。在 $1 \sim 10^6$ 中,一个数最多有 $240$ 个约数。另外当 $A$ 不是特别大时,约数个数的估计公式为 $\sqrt[3]{A}$。

  AC 代码如下,时间复杂度为 $O(nm\sqrt[3]{A} \log{A})$:

#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(g_{0,0}, g_{n-1,m-1})$ 的每一个约数 $d$,把满足 $d \mid g_{i,j}$ 的位置 $(i,j)$ 标记出来,判断能否只通过这些被标记的位置从 $(0,0)$ 走到 $(n-1,m-1)$,可以用 dp 实现,与上面的实现类似。

  AC 代码如下,时间复杂度为 $O(nm\sqrt[3]{A})$:

#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

标签:约数,GCD,int,++,grid,gcd
From: https://www.cnblogs.com/onlyblues/p/18133132

相关文章

  • 2-68. 基础数据创建 Node & GridNodes
    AStar算法概览先选FCost最小的点,如果FCost相同再选HCost最小的点回来的时候是找FCost最小的点数据结构创建Node脚本GridNodes修改MapData_SO因为地图上左下角的点是负数,这个点没有办法直接导入到数组下标中,所以需要对这个点进行处理,以便它能够映射到数......
  • DBGridEh 在粘贴中文时出现乱码和错位
    unitDBGridEh;把下面这个函数替换成这样procedureTDBGridInplaceEdit.WMPaste(varMessage:TMessage);varClipboardText:WideString;FSearchText,AText,tmpText:WideString;AColumn:TColumnEh;Idx:Integer;CanChange,TextLocated,CanTryEdit:B......
  • 【C++】gcd函数的写法
    ......
  • G. GCD on a grid
    原题链接题解\(gcd\)一定能被\(a[1][1],a[n][m]\)整除2.\(gcd\)能被通过的路径上所有元素整除由此分析:遍历\([1,\sqrt{gcd(a[1][1],a[n][m])}]\)判断能否通过(被路径上所有元素整除)我还在思考是广搜还是深搜,由于起点终点已知,求是否存在该路径,所以深搜有一个逆天优化......
  • 布局升级秘籍:掌握CSS Grid网格布局,打造响应式网页设计
    随着现代网页设计的不断演进,传统的布局方式已经逐渐不能满足设计师和开发者们对于高效、灵活且强大布局系统的追求。而CSSGrid网格布局,正是在这样的背景下应运而生的。今天,我们就来深入探讨CSSGrid布局的魅力所在,带你解锁这项强大的设计工具,让网页布局变得更加简单和高效。......
  • DataGridView 头部行高自动适应文本
    C#WinformDataGridView头部行高自动适应文本//头部行高自动适应DGV.ColumnWidthChanged+=DGV_ColumnWidthChanged;///<summary>///DataGridView头部行高自动适应文本///</summary>///<paramna......
  • WPF datagrid mvvm multi select via customize datagrid
    usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows;usingSystem.Windows.Controls;namespaceWpfApp39{publicclassMultiSelectDataGrid:D......
  • QFComponent for lazarus增加新控件TQFGridPanelComponent
    TQFGridPanelComponent控件支持在单元格绑定可视控件,运行时单元格绑定的控件会吸附到相应的单元格里。|姓名|[#][C2]单位|办公地址|备注||:-:|:-:|:-:|:-:||秋风6|[bm4]检测中心1|南山建工村1|||秋风7|检测中心2|<COMPNAME=name3>[#][c4]南山建工村2|||[c2]地址|<COMPNAME=n......
  • unistringgrid 下拉框
    unistringgrid下拉框在Delphi中,TUniStringGrid是一个用于显示文本的网格控件,它可以包含下拉框。为了在TUniStringGrid中实现下拉框,你可以使用TUniComboBox控件作为编辑控制。以下是一个简单的例子,展示如何在TUniStringGrid中添加下拉框:procedureTForm1.AddComboBoxToU......
  • StringGrid 常用操作
    StringGrid组件用于建立显示字符串的网格,与电子表格相似。它可使表格中的字符串和相关对象操作简单化。StringGrid组件提供了许多可控制网格外观念的属性,以及利用表格的结构响应用户操作的事件和方法。StringGrid具有关联对象与网格中的每个字符串的作用,这些对象为用户封装了......