📜  段树中节点的逐级交替GCD和LCM(1)

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

段树中节点的逐级交替GCD和LCM

简介

在使用线段树解决一些问题时,我们需要维护一个节点内所有元素的最大公约数(GCD)或最小公倍数(LCM)。但是在某些情况下,我们需要交替使用GCD和LCM来解决问题,这就需要我们在节点上维护逐级交替的GCD和LCM。

实现

在节点上维护逐级交替的GCD和LCM需要考虑以下几个问题:

  1. 如何计算两个数的GCD和LCM?

通常,我们可以使用欧几里得算法来计算GCD,求出a和b的余数r后,将b赋值为a,将r赋值为b,然后继续计算,一直到余数为0。最后所剩的被除数就是GCD。

对于LCM,我们可以将a和b乘起来,然后除以它们的GCD,即可得到LCM。

  1. 如何在计算逐级交替的GCD和LCM时保证顺序?

我们可以使用一个全局变量来表示当前计算的是第几个级别(从0开始)。当计算GCD时,如果此时的级别为偶数,我们就将x和y作为参数调用GCD函数;如果此时的级别为奇数,我们就将x和y作为参数调用LCM函数。反之亦然。

  1. 如何在线段树上维护逐级交替的GCD和LCM?

对于线段树上的每个节点,我们可以使用一个长度为2的数组来维护它的逐级交替GCD和LCM。数组的第一个元素代表当前节点计算的是奇数级别的GCD/LCM,第二个元素代表当前节点计算的是偶数级别的GCD/LCM。当我们需要向下递归时,我们就将当前的级别+1,然后除以2来确定下一级别的计算方式。

下面是一个示例代码:

const int MAXN = 100010;
int a[MAXN];

struct Node {
    int gcd[2], lcm[2];
} t[MAXN * 4];

void pushup(int u) {
    for(int i = 0; i < 2; ++i) {
        t[u].gcd[i] = __gcd(t[u << 1].gcd[i], t[u << 1 | 1].gcd[i ^ 1]);
        t[u].lcm[i] = t[u << 1].lcm[i] / __gcd(t[u << 1].lcm[i], t[u << 1 | 1].lcm[i ^ 1]) * t[u << 1 | 1].lcm[i ^ 1];
    }
}

void build(int u, int l, int r) {
    if(l == r) {
        t[u].gcd[0] = t[u].gcd[1] = a[l];
        t[u].lcm[0] = t[u].lcm[1] = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

Node query(int ql, int qr, int l, int r, int u, int k) {
    if(ql <= l && qr >= r) return t[u];
    int mid = (l + r) >> 1;
    if(ql > mid) return query(ql, qr, mid + 1, r, u << 1 | 1, k ^ 1);
    if(qr <= mid) return query(ql, qr, l, mid, u << 1, k ^ 1);
    Node left = query(ql, qr, l, mid, u << 1, k ^ 1);
    Node right = query(ql, qr, mid + 1, r, u << 1 | 1, k ^ 1);
    Node res;
    for(int i = 0; i < 2; ++i) {
        res.gcd[i] = __gcd(left.gcd[i], right.gcd[i ^ 1]);
        res.lcm[i] = left.lcm[i] / __gcd(left.lcm[i], right.lcm[i ^ 1]) * right.lcm[i ^ 1];
    }
    return res;
}

在上面的代码中,我们使用了一个Node结构体来表示每个节点维护的信息。在pushup函数中,我们根据左右子节点的信息更新当前节点的信息,也就是逐级交替的GCD和LCM。

在build函数中,当节点为叶子节点时,我们直接将它的GCD和LCM赋值为当前位置的值;否则,我们递归地构建左右子树,并调用pushup函数更新当前节点的GCD和LCM。

在query函数中,我们先判断当前节点是否完全包含了查询区间。如果是,我们直接返回当前节点的信息;否则,我们递归地查询左右子树,并将查询结果合并。注意,在递归查询左右子树时,我们将级别+1,然后除以2来确定下一级别的计算方式。

总结

在某些情况下,我们需要交替使用GCD和LCM来解决问题,这就需要我们在节点上维护逐级交替的GCD和LCM。我们可以使用一个全局变量来表示当前计算的是第几个级别,而在线段树上维护逐级交替的GCD和LCM,则需要递归地查询左右子树,并调用pushup函数来更新当前节点的GCD和LCM。