AtCoder-abc254_f Rectangle GCD
Rectangle GCD
线段树 + gcd 性质
辗转相减法:如果 \(a\) 和 \(b\) 互质,则 \(a + b\) 和 \(|a - b|\) 也与 \(a\) 和 \(b\) 互质
由此我们可以得到 \(gcd(a, b) = gcd(a, |a - b|)\)
考虑,在第 \(i\) 行中,从 \(a\) 列到 \(b\) 列的 \(gcd\) 值就是
\(gcd(A_i + B_a, A_i + B_{a + 1}, A_i + B_{a + 2}, ... , A_i + B_b)\)
通过上述性质改变后:
\(gcd(A_i + B_a, B_{a + 1} - B_a, B_{a + 2} - B_{a + 1}, ... , B_b - B_{b - 1})\)
我们可以发现通过这种差分的 \(gcd\) 形式,可以把原本一行的 \(gcd\) 表达式中的 \(A\) 全部删掉(除了第一个),相同地,对于每一行,其表达式都是这样
为了再次简化,我们考虑将第一列的带有 \(A_i\) 的不加入行的计算,因此可以求每一行的第 \(a + 1\) 列到第 \(b\) 列的 \(gcd\)
剩下的所有行的第一个,可以当做一个列来计算,与行的计算相同
因此对于一个矩阵的 \(gcd\),只需要计算行、列、左上角的点的 \(gcd\) 即可
这样的行列维护可以用线段树的操作进行
如果真的上面还看不懂(没有图片确实抽象),参考大佬的题解:https://zhuanlan.zhihu.com/p/524503584
注意写 \(gcd\) 的时候要记得处理 \(0\) 的情况,不然模 \(0\) 会 RE
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
int num[2][maxn], tr[2][maxn << 2];
int dif[2][maxn];
int gcd(int x, int y)
{
if(x > y) swap(x, y);
while(x)
{
int temp = x;
x = y % x;
y = temp;
}
return y;
}
void build(int now, int l, int r)
{
if(l == r)
{
tr[0][now] = dif[0][l];
tr[1][now] = dif[1][l];
return;
}
int mid = l + r >> 1;
build(now << 1, l, mid);
build(now << 1 | 1, mid + 1, r);
for(int i=0; i<2; i++)
tr[i][now] = gcd(tr[i][now << 1], tr[i][now << 1 | 1]);
}
int query(int now, int l, int r, int L, int R, int way)
{
if(L <= l && r <= R)
return tr[way][now];
int mid = l + r >> 1, ans = 0;
if(L <= mid) ans = query(now << 1, l, mid, L, R, way);
if(R > mid) ans = gcd(ans, query(now << 1 | 1, mid + 1, r, L, R, way));
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
for(int i=0; i<2; i++) for(int j=1; j<=n; j++) cin >> num[i][j];
for(int i=0; i<2; i++) for(int j=1; j<=n; j++) dif[i][j] = abs(num[i][j] - num[i][j-1]);
build(1, 1, n);
while(q--)
{
int h1, h2, w1, w2;
cin >> h1 >> h2 >> w1 >> w2;
int ans = num[0][h1] + num[1][w1];
if(h1 != h2) ans = gcd(ans, query(1, 1, n, h1 + 1, h2, 0));
if(w1 != w2) ans = gcd(ans, query(1, 1, n, w1 + 1, w2, 1));
cout << ans << endl;
}
return 0;
}