AcWing 1277. 维护序列

AcWing 1277. 维护序列

一、题目大意

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。

有长为 N 的数列,不妨设为 a1,a2,,aN

有如下三种操作形式:

  1. 把数列中的一段数全部乘一个值;
  2. 把数列中的一段数全部加一个值;
  3. 询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模 P 的值。

输入格式
第一行两个整数 NP

第二行含有 N 个非负整数,从左到右依次为 a1,a2,,aN

第三行有一个整数 M,表示操作总数;

从第四行开始每行描述一个操作,输入的操作有以下三种形式:

  • 操作 11 t g c,表示把所有满足 tigai 改为 ai×c
  • 操作 22 t g c,表示把所有满足 tigai 改为 ai+c
  • 操作 33 t g,询问所有满足 tigai 的和模 P 的值。

同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

输出格式
对每个操作 3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

数据范围
1N,M105,1tgN,0c,ai109,1P109

输入样例:

7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7

输出样例:

2
35
8

样例解释
初始时数列为 {1,2,3,4,5,6,7};

经过第 1 次操作后,数列为 {1,10,15,20,25,6,7};

对第 2 次操作,和为 10+15+20=45,模 43 的结果是 2

经过第 3 次操作后,数列为 {1,10,24,29,34,15,16};

对第 4 次操作,和为 1+10+24=35,模 43 的结果是 35

对第 5 次操作,和为 29+34+15+16=94,模 43 的结果是 8

二、解题思路

首先考虑线段树结构体需要存储哪些信息:

区间信息 l r
区间和 sum
懒标记 add mul

然后考虑懒标记的更新,有两种优先级:

  • 先加法再乘法,即(x+a)b,此时再进行一次乘法操作得到(x+a)bc是可以维护乘(x+a)b的形式的,但如果进行一次加法操作(x+a)b+c就不好维护成(x+a)b的形式了。

  • 先乘法再加法,即(xa)+b,此时再进行一次乘法操作(xa+b)c=(xac)+bc,进行一次加法操作(xa+b)+c=xa+b+c,都可以维护成(x+a)b的形式。

所以选择第二种优先级表达,即先乘法再加法,与算数运算一致。

接下来考虑操作,不妨把加法和乘法看作一次基础操作,即通通化为(xa+b),加法令a=1,乘法令b=0。所以(xa+b)c+d=xac+bc+d

void eval(Node &t, LL add, LL mul){
    t.sum = (t.sum * mul+(t.r-t.l+1)*add) % p ;// 先更新区间和信息
    t.mul = t.mul * t.mul % p;
    t.add = (t.add * mul + add ) % p;
}

然后再在pushdown中对两个儿子调用这个基础操作。

三、实现代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;
typedef long long LL;

//线段树宏定义
#define ls u << 1
#define rs u << 1 | 1

const int N = 100010;

// n:N 个非负整数   p:数模 P 的值  m:操作总数
int n, p, m;
int w[N];

struct Node {
    int l, r;
    LL sum, add, mul;
} tr[N << 2];

void pushup(int u) {
    tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % p; //更新sum和信息
}

//计算函数
void eval(Node &t, LL add, LL mul) {
    t.sum = (t.sum * mul + (t.r - t.l + 1) * add) % p;
    t.mul = t.mul * mul % p;
    t.add = (t.add * mul + add) % p;
}

void pushdown(int u) {
    eval(tr[ls], tr[u].add, tr[u].mul); //处理左儿子
    eval(tr[rs], tr[u].add, tr[u].mul); //处理右儿子
    tr[u].add = 0, tr[u].mul = 1;       //清空懒标记
}

void build(int u, int l, int r) {
    tr[u] = {l, r, w[r], 0, 1};
    if (l == r) return;
    int mid = l + r >> 1;
    build(ls, l, mid), build(rs, mid + 1, r);
    pushup(u);
}

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

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

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

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

int main() {
    //加快读入
    ios::sync_with_stdio(false), cin.tie(0);

    cin >> n >> p;
    for (int i = 1; i <= n; i++) cin >> w[i];

    //构建线段树,root=1,l=1,r=n
    build(1, 1, n);

    cin >> m;
    while (m--) {
        int t, l, r, d;
        cin >> t >> l >> r;
        if (t == 1) { //乘
            cin >> d;
            modify(1, l, r, 0, d);
        } else if (t == 2) { //加
            cin >> d;
            modify(1, l, r, d, 1);
        } else //查
            printf("%d\n", query(1, l, r));
    }
    return 0;
}
posted @   糖豆爸爸  阅读(122)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2018-04-19 Windows 10安装pip方法
2013-04-19 IntelliJ IDEA12.1.1+Tomcat 7.0.39+Jrebel 5.2.2开发及热部署
Live2D
点击右上角即可分享
微信分享提示