2025/10/27~2025/11/2 做题笔记

2025/10/27

第一代图灵机

一样的套路,考虑每一个右端点对应的最左边可以到哪里,显然是最小的 \(j\) 使得 \(\max\limits_{j \le k \le i}pre_k = j - 1\)。考虑线段树维护一个区间内的最大的答案和最大的 \(pre_i\),但是发现当 \(pushup\) 的时候不好判断右区间对整个区间的贡献,所以需要维护一个区间中右半边区间对答案的贡献。不过最后可以发现一个区间内最大的答案也没什么用,没办法只通过它算出一个区间内指定一个最小 \(pre\) 的答案,还是只能用 \(pushup\) 算出来

Code
#include <iostream>
#include <set>

using namespace std;
using ll = long long;
using pll = pair<ll, ll>;

const int kN = 2e5 + 1;

int n, m, q, c[kN];
set<int> s[kN];
ll a[kN];
struct Tr {
  int mx;
  ll val;
} tr[kN * 4];

ll Pushup(int mx, int x, int l, int r) {
  if (l == r)
    return a[l] - a[max(mx, tr[x].mx)];
  int m = (l + r) / 2;
  if (mx > tr[x * 2].mx)
    return max(a[m] - a[mx], Pushup(mx, x * 2 + 1, m + 1, r));
  return max(tr[x].val, Pushup(mx, x * 2, l, m));
}
void Update(int p, int k, int x = 1, int l = 1, int r = n) {
  if (l == r)
    return tr[x].mx = k, void();
  int m = (l + r) / 2;
  if (p <= m)
    Update(p, k, x * 2, l, m);
  else
    Update(p, k, x * 2 + 1, m + 1, r);
  tr[x].mx = max(tr[x * 2].mx, tr[x * 2 + 1].mx), tr[x].val = Pushup(tr[x * 2].mx, x * 2 + 1, m + 1, r);
}
ll Query(int mx, int nl, int nr, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return Pushup(mx, x, l, r);
  int m = (l + r) / 2;
  ll res = 0;
  if (nl <= m)
    res = Query(mx, nl, nr, x * 2, l, m);
  if (nr > m)
    res = max(res, Query(max(mx, tr[x * 2].mx), nl, nr, x * 2 + 1, m + 1, r));
  return res;
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("in", "r", stdin);
  freopen("out", "w", stdout);
#endif
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> m >> q;
  for (int i = 1; i <= m; i++)
    s[i].insert(0);
  for (int i = 1; i <= n; i++)
    cin >> a[i], a[i] += a[i - 1];
  for (int i = 1; i <= n; i++) {
    cin >> c[i];
    Update(i, *s[c[i]].rbegin()), s[c[i]].insert(i);
  }
  for (int op, l, r; q--;) {
    cin >> op >> l >> r;
    if (op - 1) {
      auto it = s[c[l]].find(l);
      if (next(it) != s[c[l]].end())
        Update(*next(it), *prev(it));
      s[c[l]].erase(l), c[l] = r;
      it = s[c[l]].lower_bound(l);
      if (it != s[c[l]].end())
        Update(*it, l);
      Update(l, *prev(it)), s[c[l]].insert(l);
    } else {
      cout << Query(l - 1, l, r) << '\n';
    }
  }
  return 0;
}

P2617 Dynamic Rankings

之前一直没有理解主席树套树状数组,现在感觉叫做树状数组套权值线段树更合适吧???

Code
#include <algorithm>
#include <iostream>
#include <map>
#include <vector>

using namespace std;

const int kN = 1e6 + 1;

int n, m, tot, cnt, a[kN], rt[kN], b[kN * 2];
vector<int> vl, vr;
map<int, int> mp;
struct Tr {
  int l, r, cnt;
} tr[kN * 20];
struct Query {
  int op, l, r, k;
} q[kN];

void Update(int& x, int p, int v, int l = 1, int r = tot) {
  x = !x ? ++cnt : x;
  tr[x].cnt += v;
  if (l == r)
    return;
  int m = (l + r) / 2;
  if (p <= m)
    return Update(tr[x].l, p, v, l, m);
  Update(tr[x].r, p, v, m + 1, r);
}
void Modify(int x, int t, int v) {
  for (; x <= n; x += x & -x)
    Update(rt[x], t, v);
}
int Query(int l, int r, int k) {
  if (l == r)
    return l;
  int m = (l + r) / 2, res = 0;
  for (int i : vl)
    res -= tr[tr[i].l].cnt;
  for (int i : vr)
    res += tr[tr[i].l].cnt;
  if (k <= res) {
    for (int& i : vl)
      i = tr[i].l;
    for (int& i : vr)
      i = tr[i].l;
    return Query(l, m, k);
  }
  for (int& i : vl)
    i = tr[i].r;
  for (int& i : vr)
    i = tr[i].r;
  return Query(m + 1, r, k - res);
}
int Ask(int l, int r, int k) {
  vl.clear(), vr.clear();
  for (; r; r -= r & -r)
    vr.push_back(rt[r]);
  for (--l; l; l -= l & -l)
    vl.push_back(rt[l]);
  int t = Query(1, tot, k);
  return b[t];
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("in", "r", stdin);
  freopen("out", "w", stdout);
#endif
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> m;
  for (int i = 1; i <= n; i++)
    cin >> a[i], b[++tot] = a[i];
  for (int i = 1; i <= m; i++) {
    char ch;
    cin >> ch >> q[i].l >> q[i].r;
    if (ch == 'Q')
      cin >> q[i].k, q[i].op = 1;
    else
      b[++tot] = q[i].r;
  }
  sort(b + 1, b + tot + 1);
  for (int i = 1; i <= tot; i++)
    mp[b[i]] = i;
  for (int i = 1; i <= n; i++)
    Modify(i, mp[a[i]], 1);
  for (int i = 1; i <= m; i++) {
    int l = q[i].l, r = q[i].r, k = q[i].k;
    if (q[i].op)
      cout << Ask(l, r, k) << '\n';
    else
      Modify(l, mp[a[l]], -1), Modify(l, mp[r], 1), a[l] = r;
  }
  return 0;
}
/*
2 4 1 3 5
*/

2025/10/28

queue

挂分原因:起始点的范围是 \(w - (n - 1) * d\) 而不是 \(n\),实在是过于唐了

Code
#include <algorithm>
#include <iostream>

using namespace std;

const int kN = 3e5 + 1;

int n, w, ans = 1e9, a[kN], b[kN];

int Calc(int x) {
  int c = 0;
  for (int i = 1; i <= n; i++)
    if (a[i] - (i - 1) * x > 0)
      b[a[i] - (i - 1) * x]++;
  int t = *max_element(b + 1, b + w - (n - 1) * x + 1);
  for (int i = 1; i <= n; i++)
    b[max(a[i] - (i - 1) * x, 0)] = 0;
  return n - t;
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("queue.in", "r", stdin);
  freopen("queue.out", "w", stdout);
#endif
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> w;
  for (int i = 1; i <= n; i++)
    cin >> a[i];
  for (int i = 0; 1 + i * (n - 1) <= w; i++)
    ans = min(ans, Calc(i));
  cout << ans;
  return 0;
}

yugo

和上次 T4 把每个点的度数变为偶数的做法完全一样,不懂为什么一个人都没过,大概是都像我一样忘记图可以不连通了吧/kk

Code
#include <iostream>
#include <vector>

using namespace std;
using pii = pair<int, int>;

const int kN = 1e6 + 1;

int T, n, m, t, ans, v[kN], d[kN], dep[kN];
vector<pii> e[kN];
struct Ed {
  int x, y, v;
} ed[kN];

void Dfs(int x, int fa, int id) {
  dep[x] = dep[fa] + 1;
  for (auto [i, id] : e[x]) {
    if (i == fa)
      continue;
    if (!dep[i])
      d[i] ^= 1, Dfs(i, x, id);
    else if (dep[i] < dep[x])
      d[x] ^= 1, ed[id].v = x;
  }
  if (d[x]) {
    if (x == t)
      ans = 1;
    else
      d[fa] ^= 1, ed[id].v = fa;
  } else
    ed[id].v = x;
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("yugo.in", "r", stdin);
  freopen("yugo.out", "w", stdout);
#endif
  cin.tie(0)->sync_with_stdio(0);
  cin >> T;
  cin >> n >> m;
  for (int i = 1, x, y; i <= m; i++)
    cin >> x >> y, ed[i] = {x, y, 0}, e[x].push_back({y, i}), e[y].push_back({x, i});
  for (int i = 1; i <= n; i++)
    if (!dep[i])
      Dfs(t = i, 0, 0);
  if (ans) {
    cout << "Yugoslavia is unstable!\n";
    return 0;
  }
  for (int i = 1; i <= m; i++) {
    int s = 2 * v[ed[i].v] + 1;
    cout << s + (ed[i].v == ed[i].y) << ' ';
    v[ed[i].v] ^= 1;
  }
  return 0;
}

2025/10/29

CF1340F Nastya and CBS

感觉思维难度也没那么难,可能是已经知道了这道题是线段树+hash吧()

肯定是一道数据结构题,考虑使用线段树维护一个区间。思考线段树应给维护哪些东西:已经匹配的括号显然没用了,那么只需要考虑剩下来的括号。因为括号不能交叉,那么有可能形成匹配的括号的区间只可能形如左边一堆左括号+右边一堆右括号。所以线段树每个区间维护的是当前区间的剩余的右括号、左括号以及该区间是否已经产生了不合法的括号对

考虑合并两个区间,会分三种情况考虑

  1. \(lson\) 的左括号多于 \(rson\) 的右括号。取出 \(lson\) 的一段后缀,如果能与 \(rson\) 完全匹配上,那么把剩余的左括号拼到 \(rson\) 的左括号上
  2. \(lson\) 的左括号少于 \(rson\) 的右括号。同理
  3. \(lson\) 的左括号等于 \(rson\) 的右括号,直接继承即可

理顺一下思路,需要的关键函数有:取出左边区间的一段后缀、取出右边区间的一段前缀、合并区间

代码对我来说很难写,一直在纠结怎么取出查询时的暂时的前后缀。看了眼巨巨宙的代码,发现前后缀直接去线段树区间上的区间去取就行了,写的是真的优雅。当然也可以暂时建几个结点,但是这样就太丑陋了

Code

#include <iostream>

using namespace std;

const int kN = 1e5 + 1, kM = 998244853, kB = 1e5 + 3, kI = 953914129;

int n, q, B[kN * 2], *b = B + kN;
struct Tr {
  int l[2], h[2];
  bool v;
} tr[kN * 4];

int LHash(int x, int len) {
  if (len == 0)
    return 0;
  if (tr[x].l[1] == len)
    return tr[x].h[1];
  if (len <= tr[x * 2 + 1].l[1])
    return LHash(x * 2 + 1, len);
  int l = tr[x * 2 + 1].l[1] - tr[x * 2 + 1].l[0], t = LHash(x * 2, len - l);
  return ((1ll * t - tr[x * 2 + 1].h[0] + kM) % kM * b[l] % kM + tr[x * 2 + 1].h[1]) % kM;
}
int RHash(int x, int len) {
  if (len == 0)
    return 0;
  if (tr[x].l[0] == len)
    return tr[x].h[0];
  if (len <= tr[x * 2].l[0])
    return RHash(x * 2, len);
  int l = tr[x * 2].l[0] - tr[x * 2].l[1], t = RHash(x * 2 + 1, len - l);
  return ((1ll * t - tr[x * 2].h[1] + kM) % kM * b[l] % kM + tr[x * 2].h[0]) % kM;
}
Tr Merge(int x, Tr ls, Tr rs) {
  int mn = min(ls.l[1], rs.l[0]);
  Tr ret = {{ls.l[0] + rs.l[0] - mn, ls.l[1] + rs.l[1] - mn}, {ls.h[0], rs.h[1]}, 1};
  if (ls.v || rs.v);
  else if (ls.l[1] > rs.l[0]) {
    if (LHash(x * 2, rs.l[0]) == rs.h[0])
      ret.h[1] = ((1ll * ls.h[1] - rs.h[0] + kM) % kM * b[rs.l[1] - rs.l[0]] % kM + rs.h[1]) % kM, ret.v = 0;
  } else if (ls.l[1] < rs.l[0]) {
    if (RHash(x * 2 + 1, ls.l[1]) == ls.h[1])
      ret.h[0] = ((1ll * rs.h[0] - ls.h[1] + kM) % kM * b[ls.l[0] - ls.l[1]] % kM + ls.h[0]) % kM, ret.v = 0;
  } else if (ls.h[1] == rs.h[0])
    ret.v = 0;
  return ret;
}
void Update(int p, int v, int x = 1, int l = 1, int r = n) {
  if (l == r) {
    if (v > 0)
      tr[x] = {{0, 1}, {0, v}, 0};
    else
      tr[x] = {{1, 0}, {-v, 0}, 0};
    return;
  }
  int m = (l + r) / 2;
  if (p <= m)
    Update(p, v, x * 2, l, m);
  else
    Update(p, v, x * 2 + 1, m + 1, r);
  tr[x] = Merge(x, tr[x * 2], tr[x * 2 + 1]);
}
Tr Query(int nl, int nr, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return tr[x];
  int m = (l + r) / 2;
  if (nl <= m && nr > m)
    return Merge(x, Query(nl, nr, x * 2, l, m), Query(nl, nr, x * 2 + 1, m + 1, r));
  if (nl <= m)
    return Query(nl, nr, x * 2, l, m);
  return Query(nl, nr, x * 2 + 1, m + 1, r);
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("in", "r", stdin);
  freopen("out", "w", stdout);
#endif
  cin.tie(0)->sync_with_stdio(0);
  b[0] = 1;
  for (int i = 1; i < kN; i++)
    b[i] = 1ll * b[i - 1] * kB % kM;
  for (int i = -1; i > -kN; i--)
    b[i] = 1ll * b[i + 1] * kI % kM;
  cin >> n >> q;
  for (int i = 1, x; i <= n; i++)
    cin >> x, Update(i, x);
  cin >> q;
  for (int op, l, r; q--;) {
    cin >> op >> l >> r;
    if (op == 1) {
      Update(l, r);
      continue;
    }
    Tr t = Query(l, r);
    cout << (t.h[0] == 0 && t.h[1] == 0 && t.v == 0 ? "Yes\n" : "No\n");
  }
  return 0;
}

P4145 上帝造题的七分钟 2 / 花神游历各国

注意到每一个点最多开 $ \mathcal{O} (1)$ 次根,当一个区间内的点全部都变成 1 后不去管他就行了。特别要注意可能 \(l > r\)

Code
#include <chrono>
#include <cmath>
#include <iostream>

using namespace std;
using ll = long long;

const int kN = 1e5 + 1;

int n, q;
ll a[kN];
struct Tr {
  bool tag;
  ll sum;
} tr[kN * 4];

void Find(int x, int l, int r) {
  if (l == r) {
    tr[x].sum = sqrt(tr[x].sum), tr[x].tag = tr[x].sum == 1;
    return;
  }
  int m = (l + r) / 2;
  if (!tr[x * 2].tag)
    Find(x * 2, l, m);
  if (!tr[x * 2 + 1].tag)
    Find(x * 2 + 1, m + 1, r);
  tr[x].sum = tr[x * 2].sum + tr[x * 2 + 1].sum, tr[x].tag = tr[x * 2].tag & tr[x * 2 + 1].tag;
}
void Update(int nl, int nr, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return Find(x, l, r);
  int m = (l + r) / 2;
  if (nl <= m)
    Update(nl, nr, x * 2, l, m);
  if (nr > m)
    Update(nl, nr, x * 2 + 1, m + 1, r);
  tr[x].sum = tr[x * 2].sum + tr[x * 2 + 1].sum, tr[x].tag = tr[x * 2].tag & tr[x * 2 + 1].tag;
}
ll Query(int nl, int nr, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return tr[x].sum;
  int m = (l + r) / 2;
  if (nl <= m && m < nr)
    return Query(nl, nr, x * 2, l, m) + Query(nl, nr, x * 2 + 1, m + 1, r);
  if (nl <= m)
    return Query(nl, nr, x * 2, l, m);
  return Query(nl, nr, x * 2 + 1, m + 1, r);
}
void Build(int x = 1, int l = 1, int r = n) {
  tr[x].tag = 0;
  if (l == r)
    return tr[x].sum = a[l], void();
  int m = (l + r) / 2;
  Build(x * 2, l, m), Build(x * 2 + 1, m + 1, r);
  tr[x].sum = tr[x * 2].sum + tr[x * 2 + 1].sum;
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("in", "r", stdin);
  freopen("out", "w", stdout);
#endif
  cin.tie(0)->sync_with_stdio(0);
  cin >> n;
  for (int i = 1; i <= n; i++)
    cin >> a[i];
  Build();
  cin >> q;
  for (int op, l, r; q--;) {
    cin >> op >> l >> r;
    if (l > r)
      swap(l, r);
    if (op)
      cout << Query(l, r) << '\n';
    else
      Update(l, r);
  }
  return 0;
}

「雅礼集训 2017 Day1」市场

虽然每个点除以 \(\log\) 次,但是还有加法操作会使得一切回到起点,这时候我们可以想到当一段区间变成 \(-1/0\) 的时候可以统一操作,这样可能是对的,但是我不会维护这个 \(tag\),遂以失败告终。

有更加巧妙的做法。考虑到一个区间要是能统一操作会很舒服,那么会想到要么使得区间中的变化量相同或者答案相同,但是答案相同显然不行,那么思考区间中的变化量相同会满足什么条件。容易发现,只有 \(max\)\(min\) 两个值是重要的,因为 \(x - \left\lfloor\dfrac{x}{d}\right\rfloor\) 是单调的,同时发现 \(max\)\(min\) 的性质非常良好,因为每有一次操作 2, \(max - min\) 会至少减少一半,同时加法操作至多只会影响两对 \(max\)\(min\),于是只用维护区间加法操作即可

貌似维护 \(max\)\(min\) 是一个套路,因为简单的加法减法不会对 \(max - min\) 产生太大的影响,但是如果是乘除法影响就会很大。

Code
#include <cmath>
#include <iostream>

using namespace std;
using ll = long long;

const int kN = 1e5 + 1;

int n, q;
struct Tr {
  ll mx, mn, sum, tag;
} tr[kN * 4];

void Func(int x, int l, int r, ll k) {
  tr[x].mx += k, tr[x].mn += k, tr[x].sum += (r - l + 1) * k, tr[x].tag += k;
}
void Pushdown(int x, int l, int r) {
  int m = (l + r) / 2;
  Func(x * 2, l, m, tr[x].tag), Func(x * 2 + 1, m + 1, r, tr[x].tag), tr[x].tag = 0;
}
void Add(int nl, int nr, int k, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return Func(x, l, r, k);
  Pushdown(x, l, r);
  int m = (l + r) / 2;
  if (nl <= m)
    Add(nl, nr, k, x * 2, l, m);
  if (nr > m)
    Add(nl, nr, k, x * 2 + 1, m + 1, r);
  tr[x].mn = min(tr[x * 2].mn, tr[x * 2 + 1].mn), tr[x].mx = max(tr[x * 2].mx, tr[x * 2 + 1].mx), tr[x].sum = tr[x * 2].sum + tr[x * 2 + 1].sum;
}
void Divide(int nl, int nr, int k, int x = 1, int l = 1, int r = n) {
  ll t = tr[x].mx - floor(1.0 * tr[x].mx / k), _t = tr[x].mn - floor(1.0 * tr[x].mn / k);
  if (nl <= l && r <= nr && t == _t)
    return Func(x, l, r, -t);
  Pushdown(x, l, r);
  int m = (l + r) / 2;
  if (nl <= m)
    Divide(nl, nr, k, x * 2, l, m);
  if (nr > m)
    Divide(nl, nr, k, x * 2 + 1, m + 1, r);
  tr[x].mn = min(tr[x * 2].mn, tr[x * 2 + 1].mn), tr[x].mx = max(tr[x * 2].mx, tr[x * 2 + 1].mx), tr[x].sum = tr[x * 2].sum + tr[x * 2 + 1].sum;
}
ll QueryMin(int nl, int nr, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return tr[x].mn;
  Pushdown(x, l, r);
  int m = (l + r) / 2;
  if (nr <= m)
    return QueryMin(nl, nr, x * 2, l, m);
  if (nl > m)
    return QueryMin(nl, nr, x * 2 + 1, m + 1, r);
  return min(QueryMin(nl, nr, x * 2, l, m), QueryMin(nl, nr, x * 2 + 1, m + 1, r));
}
ll QuerySum(int nl, int nr, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return tr[x].sum;
  Pushdown(x, l, r);
  int m = (l + r) / 2;
  if (nr <= m)
    return QuerySum(nl, nr, x * 2, l, m);
  if (nl > m)
    return QuerySum(nl, nr, x * 2 + 1, m + 1, r);
  return QuerySum(nl, nr, x * 2, l, m) + QuerySum(nl, nr, x * 2 + 1, m + 1, r);
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("in", "r", stdin);
  freopen("out", "w", stdout);
#endif
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> q;
  for (int i = 1, x; i <= n; i++)
    cin >> x, Add(i, i, x);
  for (int op, l, r, d; q--;) {
    cin >> op >> l >> r, l++, r++;
    if (op == 1)
      cin >> d, Add(l, r, d);
    else if (op == 2)
      cin >> d, Divide(l, r, d);
    else if (op == 3)
      cout << QueryMin(l, r) << '\n';
    else
      cout << QuerySum(l, r) << '\n';
  }
  return 0;
}

#228. 基础数据结构练习题

和上一题的维护方法一样

Code
#include <cmath>
#include <iostream>

using namespace std;
using ll = long long;

const int kN = 1e5 + 1;

int n, q;
struct Tr {
  ll mx, mn, sum, tag;
} tr[kN * 4];

void Func(int x, int l, int r, ll k) {
  tr[x].mx += k, tr[x].mn += k, tr[x].sum += (r - l + 1) * k, tr[x].tag += k;
}
void Pushdown(int x, int l, int r) {
  int m = (l + r) / 2;
  Func(x * 2, l, m, tr[x].tag), Func(x * 2 + 1, m + 1, r, tr[x].tag), tr[x].tag = 0;
}
void Add(int nl, int nr, int k, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return Func(x, l, r, k);
  Pushdown(x, l, r);
  int m = (l + r) / 2;
  if (nl <= m)
    Add(nl, nr, k, x * 2, l, m);
  if (nr > m)
    Add(nl, nr, k, x * 2 + 1, m + 1, r);
  tr[x].mn = min(tr[x * 2].mn, tr[x * 2 + 1].mn), tr[x].mx = max(tr[x * 2].mx, tr[x * 2 + 1].mx), tr[x].sum = tr[x * 2].sum + tr[x * 2 + 1].sum;
}
void Sqrt(int nl, int nr, int x = 1, int l = 1, int r = n) {
  ll t = tr[x].mx - floor(sqrt(tr[x].mx)), _t = tr[x].mn - floor(sqrt(tr[x].mn));
  if (nl <= l && r <= nr && t == _t)
    return Func(x, l, r, -t);
  Pushdown(x, l, r);
  int m = (l + r) / 2;
  if (nl <= m)
    Sqrt(nl, nr, x * 2, l, m);
  if (nr > m)
    Sqrt(nl, nr, x * 2 + 1, m + 1, r);
  tr[x].mn = min(tr[x * 2].mn, tr[x * 2 + 1].mn), tr[x].mx = max(tr[x * 2].mx, tr[x * 2 + 1].mx), tr[x].sum = tr[x * 2].sum + tr[x * 2 + 1].sum;
}
ll Query(int nl, int nr, int x = 1, int l = 1, int r = n) {
  if (nl <= l && r <= nr)
    return tr[x].sum;
  Pushdown(x, l, r);
  int m = (l + r) / 2;
  if (nr <= m)
    return Query(nl, nr, x * 2, l, m);
  if (nl > m)
    return Query(nl, nr, x * 2 + 1, m + 1, r);
  return Query(nl, nr, x * 2, l, m) + Query(nl, nr, x * 2 + 1, m + 1, r);
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("in", "r", stdin);
  freopen("out", "w", stdout);
#endif
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> q;
  for (int i = 1, x; i <= n; i++)
    cin >> x, Add(i, i, x);
  for (int op, l, r, d; q--;) {
    cin >> op >> l >> r;
    if (op == 1)
      cin >> d, Add(l, r, d);
    else if (op == 2)
      Sqrt(l, r);
    else
      cout << Query(l, r) << '\n';
  }
  return 0;
}
posted @ 2025-10-27 16:59  sb-yyds  阅读(9)  评论(0)    收藏  举报