XiaoHuang's Space
XiaoHuang's Space
XiaoHuang
May the force be with you.
『题解』BZOJ1798 [AHOI2009]维护序列
Posted: Dec 11, 2019
Last Modified: Dec 13, 2019
This article was last modified days ago. The content of this post may be outdated!

Portal

Portal1: BZOJ

Portal2: Luogu

Description

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

有长为$N$的数列,不妨设为$a_1, a_2, \dots , a_N$。

有如下三种操作形式:

  1. 把数列中的一段数全部乘一个值;

  2. 把数列中的一段数全部加一个值;

  3. 询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模$P$的值。

Input

第一行两个整数$N$和$P$($1 \le P \le 1000000000$)。

第二行含有N个非负整数,从左到右依次为$a_1, a_2, \dots , a_N$,($0 \le ai \le 1000000000,1 \le i \le N$)。

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

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

操作$1$:1 t g c。表示把所有满足$t \le i \le g$的$a_i$改为$ai \times c$($1 \le t \le g \le N, 0 \le c \le 1000000000$)。

操作$2$: 2 t g c。表示把所有满足$t \le i \le g$的$a_i$改为$ai + c$($1 \le t \le g \le N, 0 \le c \le 1000000000$)。

操作$3$: 3 t g。询问所有满足$t \le i \le g$的$a_i$的和模$P$的值($1 \le t \le g \le N$)。

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

Output

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

Sample Input

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

###Sample Output

2
35
8

Sample Explain

初始时数列为$(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$。

Hint

数据编号 N M
1 10 10
2 1000 1000
3 1000 1000
4 10000 10000
5 60000 60000
6 70000 70000
7 80000 80000
8 90000 90000
9 100000 100000
10 100000 100000

Solution

线段树模板题,区间加法,区间乘法,区间求和。

Code

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

using namespace std;

typedef long long LL;
const int MAXN = 500005;
int n, m, mod, opt, x, y, val;
LL a[MAXN], sum[MAXN], Mul[MAXN], Add[MAXN];
inline void pushup(LL x) {
    sum[x] = (sum[x << 1] + sum[x << 1 | 1]) % mod;
}
inline void pushdown(LL l, LL r, LL root) {//下传懒标记
    LL mid = (l + r) >> 1;
    sum[root << 1] = (sum[root << 1] * Mul[root] + Add[root] * (mid - l + 1)) % mod;
    sum[root << 1 | 1] = (sum[root << 1 | 1] * Mul[root] + Add[root] * (r - mid)) % mod;
    Mul[root << 1] = (Mul[root << 1] * Mul[root]) % mod;
    Mul[root << 1 | 1] = (Mul[root << 1 | 1] * Mul[root]) % mod;
    Add[root << 1] = (Add[root << 1] * Mul[root] + Add[root]) % mod;
    Add[root << 1 | 1] = (Add[root << 1 | 1] * Mul[root] + Add[root]) % mod;
    Mul[root] = 1; Add[root] = 0;
}
inline void build(LL l, LL r, LL root) {//建树
    Mul[root] = 1; Add[root] = 0;
    if (l==r) sum[root] = a[l]; else {
        LL mid = (l + r) >> 1;
        build(l, mid, root << 1);
        build(mid + 1, r, root << 1 | 1);
        pushup(root);
    }
    sum[root] %= mod;
}
inline void update_mul(LL ansl, LL ansr, LL val, LL l, LL r, LL root) {//区间乘法
    if (ansr < l || r < ansl) return ;
    if (ansl <= l && r <= ansr) {
        sum[root] = (sum[root] * val) % mod;
        Mul[root] = (Mul[root] * val) % mod;
        Add[root] = (Add[root] * val) % mod;
        return ;
    }
    pushdown(l, r, root);
    LL mid = (l + r) >> 1;
    update_mul(ansl, ansr, val, l, mid, root << 1);
    update_mul(ansl, ansr, val, mid + 1, r, root << 1 | 1);
    pushup(root);
}
inline void update_add(LL ansl, LL ansr, LL val, LL l, LL r, LL root) {//区间加法
    if (ansr < l || r < ansl) return ;
    if (ansl <= l && r <= ansr) {
        Add[root] = (Add[root] + val) % mod;
        sum[root] = (sum[root] + val * (r - l + 1)) % mod;
        return ;
    }
    pushdown(l, r, root);
    LL mid = (l + r) >> 1;
    update_add(ansl, ansr, val, l, mid, root << 1);
    update_add(ansl, ansr, val, mid + 1, r, root << 1 | 1);
    pushup(root);
}
inline LL query(LL ansl, LL ansr, LL l, LL r, LL root) {//区间询问
    if (ansr < l || r < ansl) return 0;
    if (ansl <= l && r <= ansr) return sum[root];
    pushdown(l, r, root);
    LL mid = (l + r) >> 1;
    return (query(ansl, ansr, l, mid, root << 1) + query(ansl, ansr, mid + 1, r, root << 1 | 1)) % mod;
}
int main() {
    scanf("%d%d", &n, &mod);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    build(1, n, 1);
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d", &opt);
        if (opt == 1) {
            scanf("%d%d%d", &x, &y, &val);
            update_mul(x, y, val, 1, n, 1);
        } else
        if (opt == 2) {
            scanf("%d%d%d", &x, &y, &val);
            update_add(x, y, val, 1, n, 1);
        } else
        if (opt == 3) {
            scanf("%d%d", &x, &y);
            printf("%lld\n", query(x, y, 1, n, 1));
        }
    }
    return 0;
}

Attachment

测试数据下载:https://www.lanzous.com/i5180sh

Article License: CC BY-NC-ND 4.0
Article Author: XiaoHuang
  1. 1. Portal
  2. 2. Description
  3. 3. Input
  4. 4. Output
  5. 5. Sample Input
  6. 6. Sample Explain
  7. 7. Hint
  8. 8. Solution
  9. 9. Code
  10. 10. Attachment
Newer Post
『题解』洛谷P4016 负载平衡问题
Older Post
『题解』BZOJ1036 [ZJOI2008]树的统计
Buy me a beer?
-->
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×