【洛谷】P3373 【模板】线段树 2

题目地址:

https://www.luogu.com.cn/problem/P3373

题目描述:
如题,已知一个数列,你需要进行下面三种操作:
将某区间每一个数乘上 x x x
将某区间每一个数加上 x x x
求出某区间每一个数的和

输入格式:
第一行包含三个整数 n , m , p n,m,p n,m,p,分别表示该数列数字的个数、操作的总个数和模数。
第二行包含 n n n个用空格分隔的整数,其中第 i i i个数字表示数列第 i i i项的初始值。
接下来 m m m行每行包含若干个整数,表示一个操作,具体如下:
操作 1 1 1:格式:1 x y k 含义:将区间 [ x , y ] [x,y] [x,y]内每个数乘上 k k k
操作 2 2 2:格式:2 x y k 含义:将区间 [ x , y ] [x,y] [x,y]内每个数加上 k k k
操作 3 3 3:格式:3 x y 含义:输出区间 [ x , y ] [x,y] [x,y]内每个数的和对 p p p取模所得的结果

输出格式:
输出包含若干行整数,即为所有操作 3 3 3的结果。

数据范围:
对于 30 % 30\% 30%的数据: n ≤ 8 n \le 8 n8 m ≤ 10 m \le 10 m10
对于 70 % 70\% 70%的数据: n ≤ 1 0 3 n \le 10^3 n103 m ≤ 1 0 4 m \le 10^4 m104
对于 100 % 100\% 100%的数据: n ≤ 1 0 5 n \le 10^5 n105 m ≤ 1 0 5 m \le 10^5 m105

参考https://blog.csdn.net/qq_46105170/article/details/121469108。代码如下:

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int n, m;
long p, a[N];
struct Node {
  int l, r;
  long sum;
  long mul, add;
} tr[N << 2];

void pushup(int u) {
  tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % p;
}

void update(Node &u, long mul, long add) {
  u.mul = (u.mul * mul) % p;
  u.add = (u.add * mul + add) % p;
  u.sum = (u.sum * mul + add * (u.r - u.l + 1)) % p;
}

void pushdown(int u) {
  update(tr[u << 1], tr[u].mul, tr[u].add);
  update(tr[u << 1 | 1], tr[u].mul, tr[u].add);
  tr[u].mul = 1, tr[u].add = 0;
}

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

void modify(int u, int l, int r, long mul, long add) {
  if (l <= tr[u].l && tr[u].r <= r) {
    update(tr[u], mul, add);
    return;
  }

  pushdown(u);
  int mid = tr[u].l + tr[u].r >> 1;
  if (l <= mid) modify(u << 1, l, r, mul, add);
  if (r > mid) modify(u << 1 | 1, l, r, mul, add);
  pushup(u);
}

long query(int u, int l, int r) {
  if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;

  pushdown(u);
  long res = 0;
  int mid = tr[u].l + tr[u].r >> 1;
  if (l <= mid) res = (res + query(u << 1, l, r)) % p;
  if (r > mid) res = (res + query(u << 1 | 1, l, r)) % p;
  return res;
}

int main() {
  scanf("%d%d%ld", &n, &m, &p);
  for (int i = 1; i <= n; i++) scanf("%ld", &a[i]);
  build(1, 1, n);
  while (m--) {
    int op, l, r;
    long x;
    scanf("%d%d%d", &op, &l, &r);
    if (op == 1) {
      scanf("%ld", &x);
      modify(1, l, r, x, 0);
    } else if (op == 2) {
      scanf("%ld", &x);
      modify(1, l, r, 1, x);
    } else printf("%ld\n", query(1, l, r));
  }
}

预处理时间复杂度 O ( n ) O(n) O(n),每个操作时间复杂度 O ( log ⁡ n ) O(\log n) O(logn),空间 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值