ABC254 F Rectangle GCD(数学 + 线段树)
F - Rectangle GCD
题目描述:
有一个\(N×N\)的平面,有两个序列\(A_i,B_i\),平面上的点\((i, j)\)对应的值是\(A_i + B_j\),有\(Q\)次询问,每次询问要查询 \(\left(h1, w2\right)\) 和 \(\left(h2, w2\right)\) 为左上和右下顶点的矩形内所有数的最大公约数是多少
思路:
首先考虑每一行所有数的最大公约数 \(\gcd\left(a_i + b_j, \gcd(a_i + b_{j + 1}, \dots , \gcd(a_i + b_k))\right)\),根据\(\gcd\)的性质可以将上述式子转化成 \(u_i = \gcd(a_i + b_j, \gcd(b_{j + 1} - b_j, \gcd(b_{j + 2} - b_{j + 1} , \dots , b_k - b_{k - 1})))\)。那么可以发现除去第一项外,都是相邻列的差值。再考虑所有的行之间的 \(\gcd\) 。\(\gcd\left(u_1, u_2, \dots , u_k\right)\) 由于每一个\(u_i\)内都有 \(\gcd\left(b_{j + 1} - b_j, \dots b_k - b_{k - 1}\right)\) ,所以可以将 \(\gcd\left(u_1, u_2, \dots u_k\right)\) 转化为 \(\gcd\left(a_i + b_j, a_{i + 1} + b_j, \dots , a_k + b_j,\gcd\left(b_{j + 1} - b_j, \dots , b_k - b_{k - 1} \right)\right)\) 再次根据 \(\gcd\) 的性质,将上述式子变成 \(\gcd\left(a_i + b_j, \gcd\left(a_{i + 1} - a_{i}, a_{i + 2} - a_{i + 1} \dots a_{k} - a_{k - 1}\right), \gcd\left(b_{j + 1} - b_j, \dots b_k - b_{k - 1}\right)\right)\)。
所以我们要做的就是求出区间的 \(\gcd\), 而能够维护区间 \(\gcd\) 的有线段树和 \(ST\) 表。用这俩维护一下行和列差值的区间 \(\gcd\) 的就可以了。
struct Seg {
const int n;
std::vector<int> val;
Seg(int n) : n(n), val(4 << std::__lg(n)) {
std::function<void(int, int, int)> build = [&](int u, int l, int r) -> void {
if (l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
val[u] = std::__gcd(val[u << 1], val[u << 1 | 1]);
};
build(1, 1, n);
}
void modify(int u, int l, int r, int pos, int x) {
if (l == r) return void(val[u] = x);
int mid = l + r >> 1;
if (mid >= pos) modify(u << 1, l, mid, pos, x);
else modify(u << 1 | 1, mid + 1, r, pos, x);
val[u] = std::__gcd(val[u << 1], val[u << 1 | 1]);
}
int query(int u, int l, int r, int ln, int rn) {
if (l >= ln && r <= rn) return val[u];
int mid = l + r >> 1;
int ans = 0;
if (mid >= ln) ans = std::__gcd(ans, query(u << 1, l, mid, ln, rn));
if (mid < rn) ans = std::__gcd(ans, query(u << 1 | 1, mid + 1, r, ln, rn));
return ans;
}
};
signed main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, q; std::cin >> n >> q;
Seg SGT1(n + 1), SGT2(n + 1);
std::vector<int> a(n + 1), b(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
for (int i = 1; i <= n; i++) {
std::cin >> b[i];
}
for (int i = 1; i < n; i++) {
SGT1.modify(1, 1, n - 1, i, std::abs(a[i + 1] - a[i]));
SGT2.modify(1, 1, n - 1, i, std::abs(b[i + 1] - b[i]));
}
for (int i = 0; i < q; i++) {
int h1, h2, w1, w2;
std::cin >> h1 >> h2 >> w1 >> w2;
int res = a[h1] + b[w1];
h2--, w2--;
int ret = 0;
ret = std::__gcd(h1 <= h2 ? SGT1.query(1, 1, n - 1, h1, h2) : 0, w1 <= w2 ? SGT2.query(1, 1, n - 1, w1, w2) : 0);
std::cout << std::__gcd(res, ret) << "\n";
}
return 0 ^ 0;
}
由于这个不存在任何的修改操作,所以也可以选择常数更小更好写的 \(ST\) 表来实现
int ga[20][200010], gb[20][200010];
int geta(int l, int r) {
if (l > r) return 0;
int k = std::__lg(r - l + 1);
return std::__gcd(ga[k][l], ga[k][r - (1 << k) + 1]);
}
int getb(int l, int r) {
if (l > r) return 0;
int k = std::__lg(r - l + 1);
return std::__gcd(gb[k][l], gb[k][r - (1 << k) + 1]);
}
signed main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, q; std::cin >> n >> q;
std::vector<int> a(n + 1), b(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
for (int i = 1; i <= n; i++) {
std::cin >> b[i];
}
int len = std::__lg(n) + 1;
for (int i = 1; i < n; i++) {
ga[0][i] = std::abs(a[i + 1] - a[i]);
gb[0][i] = std::abs(b[i + 1] - b[i]);
}
for (int i = 1; i < len; i++) {
for (int j = 1; j <= n - (1 << i) + 1; j++) {
ga[i][j] = std::__gcd(ga[i - 1][j], ga[i - 1][j + (1 << (i - 1))]);
gb[i][j] = std::__gcd(gb[i - 1][j], gb[i - 1][j + (1 << (i - 1))]);
}
}
for (int i = 0; i < q; i++) {
int h1, h2, w1, w2;
std::cin >> h1 >> h2 >> w1 >> w2;
int res = a[h1] + b[w1];
int ret = 0;
ret = std::__gcd(geta(h1, h2 - 1), getb(w1, w2 - 1));
std::cout << std::__gcd(res, ret) << "\n";
}
return 0 ^ 0;
}

浙公网安备 33010602011771号