📜  GCD 为 1 的最小子阵列 |段树(1)

📅  最后修改于: 2023-12-03 15:00:53.036000             🧑  作者: Mango

GCD为1的最小子数组 |段树

简介

题目描述:给定一个长度为n的正整数序列,要求找到其中长度最小的子数组,其GCD为1。

这个问题可以使用线段树来解决,时间复杂度为O(nlogn)。

算法思路

对于每一个节点,我们维护三个信息:

  1. 最小数a;
  2. 最大数b;
  3. 这段区间的GCD。

对于当前节点,我们首先检查这个节点代表的区间是否存在满足题意的子数组,如果不存在,就返回这个节点所维护的信息。如果存在,就递归到左儿子或右儿子中去查找。如果左右儿子区间的GCD的最大公约数为1,那么我们可以直接返回这个左右儿子区间中离左右端点较近的那个点的信息,否则我们就返回这个左右儿子区间GCD的信息。

代码如下:

struct Node {
    int l, r;
    int mi, mx;
    int gcd;

    void update(const Node &L, const Node &R) {
        mi = min(L.mi, R.mi);
        mx = max(L.mx, R.mx);
        gcd = __gcd(L.gcd, R.gcd);
    }
} t[N << 2];

void build(int p, int l, int r) {
    if (l == r) {
        t[p] = {l, r, a[l], a[l], a[l]};
    } else {
        int m = (l + r) >> 1;
        build(p << 1, l, m);
        build(p << 1 | 1, m + 1, r);
        t[p].l = l;
        t[p].r = r;
        t[p].update(t[p << 1], t[p << 1 | 1]);
    }
}

Node query(int p, int l, int r) {
    if (l <= t[p].l && t[p].r <= r) {
        return t[p];
    } else {
        int m = (t[p].l + t[p].r) >> 1;
        if (r <= m) {
            return query(p << 1, l, r);
        } else if (l > m) {
            return query(p << 1 | 1, l, r);
        } else {
            Node res;
            res.l = l;
            res.r = r;
            res.update(query(p << 1, l, r), query(p << 1 | 1, l, r));
            // 如果当前区间的gcd为1,就返回当前区间
            if (res.gcd == 1) {
                return res;
            } else {
                // 如果左右区间的gcd的最大公约数为1,就返回离左右端点最近的那个点的信息
                Node L = query(p << 1, l, m);
                Node R = query(p << 1 | 1, m + 1, r);
                if (__gcd(L.gcd, R.gcd) == 1) {
                    if (R.l - L.r == 1) {
                        res = L;
                        res.update(L, R);
                    }
                }
            }
            return res;
        }
    }
}
总结

以上就是本题的解题思路,通过线段树来实现。这个题目的时间复杂度是O(nlogn),所以在数据量较大的情况下,还是比较快的。本题的主要思路就是对于每一个节点,维护三个信息:最小值、最大值和GCD,然后通过递归和左右两个子节点的GCD的最大公约数来判断是否可以结束递归,并返回最小的子数组。