首页 > 其他分享 >洛谷 P3373 总结

洛谷 P3373 总结

时间:2023-09-01 15:23:05浏览次数:48  
标签:总结 qr 洛谷 int tr mid P3373 ans ql

洛谷 P3373

题意

  • 给定长度为 \(n\) 的整数序列,有以下三种操作共 \(q\) 次:
    • 将区间 \([l, r]\) 每一个数乘上 \(k\);
    • 将区间 \([l, r]\) 每一个数加上 \(k\);
    • 求出区间 \([l, r]\) 的区间和对 \(m\) 取模后的结果。
  • \(1 \leqslant n, q \leqslant 10^5\)。

思路

  • 这个题非常明确的是要维护区间问题,我们自然而然可以想到线段树,而且维护的东西也比较明白,只用维护答案 \(ans\) 即可。

  • 不过此题的难点不是线段树要维护什么,而是懒标记该怎么打?我们发现这里有加和乘两种操作,如果把两种分开的话显然会出现问题,因为乘法和加法之间是没有交换律的,那该怎么办?

  • 我们发现如果完全分开是不切实际的,所以我们可以将其化为一种比较方便于更新 \(ans\) 和懒标记的形式,比如在这里懒标记就可以表示为将原本的数乘上 \(a\) 再加 \(b\)。也就是如果原本的 \(ans = x\),那么懒标记传到此处后,\(ans = ax + b(r - l + 1)\),因为区间内的每个元素都会乘 \(a\) 再加 \(b\),那么它们的和肯定会先乘 \(a\) 再加 \(b \times 元素数量\),即 \(r - l + 1\)。

  • 假设原本的懒标记为 \(a,b\),从上面传下来的为 \(a', b'\),那么新的 \(a\) 会变为 \(a \cdot a'\),新的 \(b\) 会变为 \(b \cdot a' + b'\)。

  • 那么如果某结点懒标记向下传了,那么现在这个结点的懒标记应该是什么呢?显然现在的懒标记是要让传下去后的信息和没传一样,也就是 \(\begin{cases} a = 1 \\ b = 0 \end{cases}\) 了,这也是最开始懒标记的初始化。

时间复杂度

线段树维护区间信息,单次 \(\log n\),共 \(q\) 次,总时间复杂度:\(O(q \log n)\)。

Code

点击查看代码
#include <iostream>

using namespace std;

const int MAXN = 1e5 + 5;

struct Node {
  int ans, lazy1, lazy2;
} tr[MAXN * 4];

int n, q, m, a[MAXN];

Node Merge(Node l, Node r) {
  return {(l.ans + r.ans) % m, 1, 0};  // 清空之后的懒标记
}

void update(int x, int l, int r, int a, int b) {  // 更新答案及懒标记
  tr[x].ans = (1ll * a * tr[x].ans + 1ll * b * (r - l + 1)) % m;
  tr[x].lazy1 = 1ll * tr[x].lazy1 * a % m;
  tr[x].lazy2 = (1ll * tr[x].lazy2 * a + b) % m;
}

void Pushdown(int x, int l, int r) {
  int mid = (l + r) >> 1;
  update(x * 2, l, mid, tr[x].lazy1, tr[x].lazy2);
  update(x * 2 + 1, mid + 1, r, tr[x].lazy1, tr[x].lazy2);
  tr[x] = {tr[x].ans, 1, 0};
}

void init(int i, int l, int r) {
  if (l == r) {
    tr[i] = {a[l], 1, 0};
    return ;
  }
  int mid = (l + r) >> 1;
  init(i * 2, l, mid), init(i * 2 + 1, mid + 1, r);
  tr[i] = Merge(tr[i * 2], tr[i * 2 + 1]);
}

void modify(int i, int l, int r, int ql, int qr, int a, int b) {
  if (l > qr || r < ql) {
    return ;
  } else if (ql <= l && r <= qr) { 
    update(i, l, r, a, b);
    return ;
  }
  Pushdown(i, l, r);
  int mid = (l + r) >> 1;
  modify(i * 2, l, mid, ql, qr, a, b);
  modify(i * 2 + 1, mid + 1, r, ql, qr, a, b);
  tr[i] = Merge(tr[i * 2], tr[i * 2 + 1]);
}

Node query(int i, int l, int r, int ql, int qr) {
  if (l > qr || r < ql) {
    return {0, 0, 0};  // 让答案不受影响
  } else if (ql <= l && r <= qr) {
    return tr[i];
  }
  Pushdown(i, l, r);
  int mid = (l + r) >> 1;
  return Merge(query(i * 2, l, mid, ql, qr), query(i * 2 + 1, mid + 1, r, ql, qr));
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> q >> m;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  init(1, 1, n);
  for (int t, l, r, k; q--; ) {
    cin >> t >> l >> r;
    if (t == 1) {
      cin >> k;
      modify(1, 1, n, l, r, k, 0);  // 单纯的乘法是 a = k, b = 0
    } else if (t == 2) {
      cin >> k;
      modify(1, 1, n, l, r, 1, k);  // 单纯的加法是 a = 1, b = k
    } else {
      cout << query(1, 1, n, l, r).ans << '\n';
    }
  }
  return 0;
}

小贴士:此题为线段树习题,详情见:线段树与树状数组

标签:总结,qr,洛谷,int,tr,mid,P3373,ans,ql
From: https://www.cnblogs.com/xhr0817-blog/p/17671974.html

相关文章

  • 状压DP总结
    动态规划-状压类-总结状压类的题,一般都需要用到二进制的性质。(用到组合数概率也不小)母题2:考虑用二进制表示摆放方式,然后使用位运算判断攻击。变式有一位很小,状压,状态类似于母题。变式2有交换操作,所以与逆序对相关,然后数学讨论一下,再状压。变式3考虑用集合、余数表示,注意......
  • 源代码泄露总结
    .git源码泄露.git信息泄露漏洞-简明原理及利用方法-FreeBuf网络安全行业门户Git信息泄露原理解析及利用总结-FreeBuf网络安全行业门户commit、tree和blob三个对象之间的关系|Git(geek-docs.com)参考源于上面师傅文章,引用以作学习之用!git简单使用Git常用命令实操1......
  • 8月pyyz考试总结
    【20230801暑期提高特训营】摸底考试#1T1-三值的排序\(100pts\)Solution可以考虑预处理三个值的个数。如果当前的值不在正确的位置,则在其应当在的位置范围内寻找是否有当前位置上应该放置的数字。若有则直接交换,若没有则任选数字交换,并统计答案。T2-日记\(100pts\)Solu......
  • 开学总结
    我从7月8号开始放假,9号找兄弟玩了一天,10号就开始正式上班,本来8月初就能结课,可惜老天爷下了几天暴雨。其次我考了科目二,这些加起来一共花了一个月是时间。我正式开始学习是在8月12日,虽然学的东西不多,但还是想总结一下。截至到今天已经学了半个月了。学习收获:学习了linux的基础语......
  • uniapp 项目实践总结(二)从零开始搭建一个项目
    导语:本篇文章主要是项目方面的技术开发总结,新建一个项目可以选择使用可视化界面,也可以使用命令行搭建。目录可视化界面命令行搭建安卓开发环境苹果开发环境可视化界面安装软件使用官方推荐的HbuilderX软件,开发方式比较简单,内置相关环境以及终端,无需配置node。下......
  • P3373 【模板】线段树 2
    【模板】线段树2如题,已知一个数列,你需要进行下面三种操作:将某区间每一个数乘上\(x\);将某区间每一个数加上\(x\);求出某区间每一个数的和。输入格式第一行包含三个整数\(n,q,m\),分别表示该数列数字的个数、操作的总个数和模数。第二行包含\(n\)个用空格分隔的整数,其......
  • 传输层协议总结
    传输层就是在信纸的空白上写上新的“收信人”信息。每一所房子【某一个终端】会配备一个管理员(传输层协议)。管理员从邮差手中接过信,会根据“收信人”,将信送给房子中的某个人。使用端口号(portnumber)来识别收信人(某个进程)。传输层协议TCP面向字节流服务面向连接,可靠,......
  • 洛谷p1824 进击的奶牛
    P1824进击的奶牛题目描述FarmerJohn建造了一个有N(2<=N<=100,000)个隔间的牛棚,这些隔间分布在一条直线上,坐标是x1,...,xN(0<=xi<=1,000,000,000)。他的C(2<=C<=N)头牛不满于隔间的位置分布,它们为牛棚里其他的牛的存在而愤怒。为了防止牛之间的互相打斗,FarmerJohn想把这些......
  • java基础(根据狂神总结)
    java基础(狂神)注释单行//多行/**/文档注释(可以加参数)/****/***@Descriptionhelloworld*@Authorcheems*/}数据类型类型基本数据类型数值类整数(查看最大字节大小,通过对应的类的源码看)byte占1个字节short2in......
  • 32位数字电位器AD5228使用及调试总结
    一概念什么是数字电位计?  数字电位器(DigitalPotentiometer)亦称数控可编程电阻器,是一种代替传统机械电位器(模拟电位器)的新型CMOS数字、模拟混合信号处理的集成电路。数字电位器由数字输入控制,产生一个模拟量的输出。依据数字电位器的不同,抽头电流最大值可以从几百微安到几个......