📅  最后修改于: 2023-12-03 15:40:39.198000             🧑  作者: Mango
在使用线段树解决一些问题时,我们需要维护一个节点内所有元素的最大公约数(GCD)或最小公倍数(LCM)。但是在某些情况下,我们需要交替使用GCD和LCM来解决问题,这就需要我们在节点上维护逐级交替的GCD和LCM。
在节点上维护逐级交替的GCD和LCM需要考虑以下几个问题:
通常,我们可以使用欧几里得算法来计算GCD,求出a和b的余数r后,将b赋值为a,将r赋值为b,然后继续计算,一直到余数为0。最后所剩的被除数就是GCD。
对于LCM,我们可以将a和b乘起来,然后除以它们的GCD,即可得到LCM。
我们可以使用一个全局变量来表示当前计算的是第几个级别(从0开始)。当计算GCD时,如果此时的级别为偶数,我们就将x和y作为参数调用GCD函数;如果此时的级别为奇数,我们就将x和y作为参数调用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。