【势能线段树维护区间与x取gcd】

【2025上海市赛】Djangle 的数据结构

题意

给你一个正整数序列 $ a_1, a_2, \ldots, a_n $,要求你支持以下操作:

  1. 操作 0 $ (l, r, x) $:将区间 \([l, r]\) 的所有元素赋值为 $ x $。
  2. 操作 1 $ (l, r, x) $:
    • 先计算区间 \([l, r]\) 中每个元素与 $ x $ 的 gcd 之和(即 \(\sum_{i=l}^{r} \gcd(a_i, x)\))。
    • 然后将该区间每个元素更新为 \(\gcd(a_i, x)\)

对于每个操作 1,需要输出计算得到的 gcd 之和。

题解

用线段树来维护每个区间的和(Sum)、最大公约数(Gcd)和最小公倍数(Lcm)。对于区间赋值操作(操作 0),直接通过懒惰标记进行批量更新。对于 gcd 查询与更新操作(操作 1),利用以下性质进行优化:

  • 如果当前节点的 Lcm 能整除给定的 $ x $,则 $ \gcd(a_i, x) = a_i $ 对所有元素成立,因此无需更新,直接跳过。
  • 如果当前节点所有元素相等(即 $ \text{Gcd} = \text{Lcm} $),则批量更新为 $ \gcd(\text{Gcd}, x) $。
  • 否则,递归处理子节点,直到叶子节点单独计算。

每次操作 1 先执行更新,然后查询区间和作为结果。通过 Lcm 整除检查和批量更新,减少了不必要的递归,保证了效率。时间复杂度为 $ O(n \log^3 n) $,但由于数据规模限制(总 $ n $ 和 $ q $ 不超过 $ 10^5 $),该方法可以高效处理。

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

template<class Node>
struct SegmentTree {
#define lc u<<1
#define rc u<<1|1
    const int n, N;
    vector<Node> tr;

    SegmentTree(): n(0) {}
    SegmentTree(int n_): n(n_), N(n * 4 + 10) {
        tr.reserve(N);
        tr.resize(N);
    }
    SegmentTree(vector<int> init) : SegmentTree(init.size()) {
        function<void(int, int, int)> build = [&](int u, int l, int r) {
            tr[u].l = l, tr[u].r = r;
            init_lazy(tr[u]);
            if (l == r) {
                tr[u] = {l, r, 0, init[l - 1], init[l - 1], init[l - 1]};
                return ;
            }
            i64 mid = (l + r) >> 1;
            build(lc, l, mid);
            build(rc, mid + 1, r);
            pushup(tr[u], tr[lc], tr[rc]);
        };
        build(1, 1, n);
    }

    void cal_lazy(Node & fa, Node & ch) {
        i64 b = fa.cov;
        ch.Sum = b * (ch.r - ch.l + 1);
        ch.Gcd = ch.Lcm = b;
    }

    void tag_union(Node& fa, Node& ch) {
        i64 b = fa.cov;
        ch.cov = b;
    }

    void init_lazy(Node& u) {
        u.cov = 0;
    }

    void pushdown(i64 u) {
        if (tr[u].cov != 0) {
            cal_lazy(tr[u], tr[lc]);
            cal_lazy(tr[u], tr[rc]);
            tag_union(tr[u], tr[lc]);
            tag_union(tr[u], tr[rc]);
            init_lazy(tr[u]);
        }
    }

    void pushup(Node& U, Node& L, Node& R) { //上传
        U.l = L.l,U.r = R.r;
        U.Sum = L.Sum + R.Sum;
        U.Gcd = gcd(L.Gcd, R.Gcd);
        U.Lcm = lcm(L.Lcm, R.Lcm);
    }

    void modify(int u, int l, int r, i64 k) {
        if (tr[u].l >= l && tr[u].r <= r) {
            tr[u].cov = k;
            tr[u].Sum = k * (tr[u].r - tr[u].l + 1);
            tr[u].Gcd = tr[u].Lcm = k;
            return ;
        }
        pushdown(u);
        int mid = (tr[u].l + tr[u].r) >> 1;
        if (l <= mid) {
            modify(lc, l, r, k);
        }
        if (r > mid) {
            modify(rc, l, r, k);
        }
        pushup(tr[u], tr[lc], tr[rc]);
    }

    void modify2(int u, int l, int r, i64 k) {
        if (k % tr[u].Lcm == 0 || tr[u].r < l || tr[u].l > r) {
            return ;
        }
        if (tr[u].l == tr[u].r) {
            tr[u].Sum = gcd(k, tr[u].Sum);
            tr[u].Gcd = tr[u].Lcm = tr[u].Sum;
            return ;
        }
        if (l <= tr[u].l && tr[u].r <= r && tr[u].Gcd == tr[u].Lcm) {
            k = gcd(k, tr[u].Gcd);
            tr[u].cov = k;
            tr[u].Sum = k * (tr[u].r - tr[u].l + 1);
            tr[u].Gcd = tr[u].Lcm = k;
            return ;
        }
        pushdown(u);
        modify2(lc, l, r, k);
        modify2(rc, l, r, k);
        pushup(tr[u], tr[lc], tr[rc]);
    }

    Node query(int u, int l, int r) { //区查
        if (l <= tr[u].l && tr[u].r <= r) {
            return tr[u];
        }
        i64 mid = tr[u].l + tr[u].r >> 1;
        pushdown(u);
        if (r <= mid) {
            return query(lc, l, r);
        }
        if (l > mid) {
            return query(rc, l, r);
        }
        Node U;
        Node L = query(lc, l, r), R = query(rc, l, r);
        pushup(U, L, R);
        return U;
    }
};

struct Node { //线段树定义
    int l, r, cov;
    i64 Sum, Gcd, Lcm;
};

void solve() {

    int n, q;
    cin >> n >> q;

    vector<int> a(n);
    for (int i = 0; i < n; i += 1) {
        cin >> a[i];
    }

    SegmentTree<Node> S(a);
    while (q--) {
        int op, l, r, x;
        cin >> op >> l >> r >> x;
        if (op == 0) {
            S.modify(1, l, r, x);
        }
        else {
            S.modify2(1, l, r, x);
            auto ans = S.query(1, l, r).Sum;
            cout << ans << "\n";
        }
    }

}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}
posted @ 2025-09-03 15:53  Ke_scholar  阅读(41)  评论(0)    收藏  举报