首页 > 其他分享 >P3373 【模板】线段树 2

P3373 【模板】线段树 2

时间:2023-08-29 21:14:27浏览次数:35  
标签:rs int 线段 tr mu add ls P3373 模板

【模板】线段树 2

如题,已知一个数列,你需要进行下面三种操作:

  • 将某区间每一个数乘上 \(x\);
  • 将某区间每一个数加上 \(x\);
  • 求出某区间每一个数的和。

输入格式

第一行包含三个整数 \(n,q,m\),分别表示该数列数字的个数、操作的总个数和模数。

第二行包含 \(n\) 个用空格分隔的整数,其中第 \(i\) 个数字表示数列第 \(i\) 项的初始值。

接下来 \(q\) 行每行包含若干个整数,表示一个操作,具体如下:

操作 \(1\): 格式:1 x y k 含义:将区间 \([x,y]\) 内每个数乘上 \(k\)

操作 \(2\): 格式:2 x y k 含义:将区间 \([x,y]\) 内每个数加上 \(k\)

操作 \(3\): 格式:3 x y 含义:输出区间 \([x,y]\) 内每个数的和对 \(m\) 取模所得的结果

输出格式

输出包含若干行整数,即为所有操作 \(3\) 的结果。

样例输入 #1

5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4

样例输出 #1

17
2

提示

【数据范围】

对于 \(30\%\) 的数据:\(n \le 8\),\(q \le 10\)。
对于 \(70\%\) 的数据:$n \le 10^3 \(,\)q \le 10^4$。
对于 \(100\%\) 的数据:\(1 \le n \le 10^5\),\(1 \le q \le 10^5\)。

除样例外,\(m = 571373\)。

(数据已经过加强 _

样例说明:

故输出应为 \(17\)、\(2\)(\(40 \bmod 38 = 2\))。

解题思路

那么这道题有些颠覆了我对懒标记的认识,首先加和乘混在一起肯定要注意次序,那我们就以先加再乘为例,原\(a_1,a_2,a_3,\dots,a_n\)

变为\(a_1+t,a_2+t,a_3+t,\dots,a_n+t\),乘上\(x\)后变为\(a_1x+tx,a_2x+tx,a_3x+tx,\dots,a_nx+tx\),那可以发现这是一个乘法分配律的过程,那么对于加的懒标记不仅要加上\(t\),而且要乘\(x\),而对于乘的懒标记则要乘上\(x\),所以说

#include <bits/stdc++.h>

using namespace std;
const int N = 1000010;

int a[N]; // 原数组
int n, m, p;

// 线段树模板
#define int long long
#define ls u << 1
#define rs u << 1 | 1
#define mid ((l + r) >> 1)

struct Node {
    int l, r;
    int sum;
    int mu, add;
} tr[N];

void pushup(int u) {
    tr[u].sum = (tr[ls].sum + tr[rs].sum) % p;
}

void build(int u, int l, int r) {
    tr[u].l = l, tr[u].r = r, tr[u].mu = 1;

    if (l == r) {
        tr[u].sum = a[l];
        return;
    }
    build(ls, l, mid), build(rs, mid + 1, r);
    pushup(u);
}

void pushdown(int u) {
    if (tr[u].add == 0 && tr[u].mu == 1) return; // 默认懒标记

    int &mu = tr[u].mu, &add = tr[u].add; // 此时的add懒标记,已经处理过了

    tr[ls].sum = (mu * tr[ls].sum % p + (tr[ls].r - tr[ls].l + 1) * add % p) % p;
    tr[rs].sum = (mu * tr[rs].sum % p + (tr[rs].r - tr[rs].l + 1) * add % p) % p;

    tr[ls].mu = tr[ls].mu * mu % p;
    tr[rs].mu = tr[rs].mu * mu % p;

    tr[ls].add = (tr[ls].add * mu % p + add) % p;
    tr[rs].add = (tr[rs].add * mu % p + add) % p;

    mu = 1, add = 0; // 清空懒标记
}

void add(int u, int L, int R, int v) {
    int l = tr[u].l, r = tr[u].r;
    if (l >= L && r <= R) {
        tr[u].add = (tr[u].add + v) % p;
        tr[u].sum = (tr[u].sum + v * (tr[u].r - tr[u].l + 1) % p) % p;
        return;
    }
    if (l > R || r < L) return;
    pushdown(u);
    add(ls, L, R, v), add(rs, L, R, v);
    pushup(u);
}

void mu(int u, int L, int R, int v) {
    int l = tr[u].l, r = tr[u].r;
    if (l >= L && r <= R) {
        tr[u].add = tr[u].add * v % p; // 比较重要的一步,add要在这里乘上v,因为后面可能要加其他的数而那些数其实是不用乘k的
        tr[u].mu = tr[u].mu * v % p;
        tr[u].sum = tr[u].sum * v % p;
        return;
    }
    if (l > R || r < L) return;
    pushdown(u);
    mu(ls, L, R, v), mu(rs, L, R, v);
    pushup(u);
}

int query(int u, int L, int R) {
    int l = tr[u].l, r = tr[u].r;
    if (l >= L && r <= R) return tr[u].sum;
    if (l > R || r < L) return 0;
    pushdown(u);
    return (query(ls, L, R) + query(rs, L, R)) % p;
}

signed main() {
// 文件输入输出
#ifndef ONLINE_JUDGE
    freopen("P3373.in", "r", stdin);
#endif
    // 加快读入
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> m >> p;
    for (int i = 1; i <= n; i++) cin >> a[i];

    build(1, 1, n);

    while (m--) {
        int op, l, r;
        cin >> op >> l >> r;
        if (op == 1) {
            int k;
            cin >> k;
            mu(1, l, r, k);
        } else if (op == 2) {
            int k;
            cin >> k;
            add(1, l, r, k);
        } else
            printf("%lld\n", query(1, l, r));
    }
    return 0;
}

标签:rs,int,线段,tr,mu,add,ls,P3373,模板
From: https://www.cnblogs.com/littlehb/p/17665847.html

相关文章

  • Daimayuan Online Judge 线段树打标记2
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1lrd,令所有的\(a_i(l\leqi\leqr)\)加上\(d\)。2lrd,令所有的\(a_i(l\leqi\leqr)\)乘上\(d\)。3lrd,令所有的\(a_i(l\leqi\leqr)\)等于\(d\)。4lr,查询\((\sum_{i=l......
  • Daimayuan Online Judge 线段树打标记1
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1lrd,令所有的\(a_i(l\leqi\leqr)\)加上\(d\)。2lr,查询\(max_{i=l}^{r}a_i\)。区间修改的线段树要比基础线段树多考虑一个元素:\(lazy\tag\)。复杂的信息可以用多个标记表示。\(lazy\ta......
  • Daimayuan Online Judge 线段树2
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1xd,修改\(a_x=d\)。2lr,查询\([l,r]\)中的最大子段和。一:确定需要维护的信息。根据分治中线讨论,哪些信息可以合并出所需信息。递归讨论新信息如何合并。直至完全拆解。不越过分治中线:\([l,r]\)......
  • Daimayuan Online Judge 线段树1
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1.1xd,修改\(a_x=d\)。2.2lr,查询\(min_{i=l}^{r}a_i\),并输出\(\sum_{i=l}^{r}[a_i=min_{i=l}^{r}a_i]\)。一:确定出需要维护的信息\(Info\)。建立线段树节点structInfo{ intminv......
  • Vue3 Refs模板
    Refs模板用来获取页面DOM元素或者组件,类似于Vue2.X中的$refs。Refs模板的使用方法如下。(1)在setup()中创建ref对象,其值为null。(2)为元素添加ref属性,其值为步骤(1)中创建的ref对象名。(3)完成页面渲染之后,获取DOM元素或者组件。 src\views\HomeView.vue<template><d......
  • 线段树
    下饭写错合集:if(l>R||r<L)return0;写成if(l>=R||r<=L)return0;t1[p]没有初始化为\(1\)。忘记建树QAQ。线段树是解决RMQ问题中的一种常用的数据结构,树状数组能实现的功能是线段树能实现功能的子集。线段树可以在\(O(\logn)\)内实现。单点/区间修改(加,......
  • 代码模板
    今天有空来专门总结一下代码模版,顺便定制一张代码模版鼠标垫,哦吼!!!↑这就是预期效果啦!!!下面开始总结算法模版:素数筛(线性筛)时间复杂度\(O(N)\)voidprimes(intn){//线性筛区间[1,n]的素数 memset(isprime,true,sizeof(isprime));//全部标记为素数isprime[1]=false;m......
  • Galaxy Studio星河工作室于8月26日正式使用Canva可画网页版对室徽进行重绘!还开放了名
    GalaxyStudio星河工作室于8月26日正式使用Canva可画网页版对室徽进行重绘!还开放了名片模板?据了解,GalaxyStudio星河工作室室长于8月26日对使用Canva可画网页版室徽正式重绘!新室徽到底怎么样呢?让大家都来看看吧!怎么样?不错吧!对了!还有一个名片模板,想要使用的成员可以找QQ2789617......
  • Qt开发思想探幽]QObject、模板继承和多继承
    @目录[Qt开发探幽]QObject、模板继承和多继承1.QObject为什么不允许模板继承:2.如果需要使用QObject进行多继承的话,子对象引用的父类链至多只能含有一个QObject3.如果使用模板类和QObject做多继承,编译不通过问题场景[Qt开发探幽]QObject、模板继承和多继承当我们在用Qt开发一个......
  • Python HTML 的模板引擎 Jinja2
    PythonJinja2是一个用于生成动态HTML的模板引擎。它可以让你在HTML中使用Python的语法和逻辑,从而实现数据和视图的分离。本文将介绍PythonJinja2的基本用法和特性,以及一些实例和技巧。安装和导入要使用PythonJinja2,你需要先安装它。你可以使用pip命令来安装:pip......