【势能线段树维护区间与x取gcd】
【2025上海市赛】Djangle 的数据结构
题意
给你一个正整数序列 $ a_1, a_2, \ldots, a_n $,要求你支持以下操作:
- 操作 0 $ (l, r, x) $:将区间 \([l, r]\) 的所有元素赋值为 $ x $。
- 操作 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;
}

浙公网安备 33010602011771号