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;
}
posted @ 2022-09-29 10:53  浅渊  阅读(56)  评论(0)    收藏  举报