[Ahoi2009]Seq 维护序列seq

Problem : [Ahoi2009]Seq 维护序列seq

Time Limit: 2 Sec   Memory Limit: 128 MB
Submit: 0   Solved: 0
[ Submit][ Status]

Description

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,…,aN 。有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。

Input

第一行两个整数N和P(1≤P≤1000000000)。第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。第三行有一个整数M,表示操作总数。从第四行开始每行描述一个操作,输入的操作有以下三种形式: 操作1:“1 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai×c (1≤t≤g≤N,0≤c≤1000000000)。 操作2:“2 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai+c (1≤t≤g≤N,0≤c≤1000000000)。 操作3:“3 t g”(不含双引号)。询问所有满足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

Output

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

Sample Input

7 431 2 3 4 5 6 751 2 5 53 2 42 3 7 93 1 33 4 7

Sample Output

2358

HINT

【样例说明】

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


测试数据规模如下表所示

N<=100000
M<=100000



处理两个lazy标记的时候犹豫了一下,总感觉先乘或者先加有本质的区别,不知道先处理哪一个lazy标记。后来发现做乘法的时候,只要更新sum和mul之外,再更新add标记,这样在最后pushdown的时候只要先乘后加就可以了。

sum[rt]表示区间和,mul[rt]表示乘法的lazy标记,add[rt]表示加的lazy标记。( x1 + x2 + x3 + x4 + add * 4) * mul 等价于 (x1 + x2 + x3 + x4) * mul + (add * mul) * 4。(add*mul就是做乘法的时候更新add的结果,add = add * mul)



#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define ls rt << 1
#define rs rt << 1|1
const int maxn = 100000 + 100;
int n;
ll mod;
ll sum[maxn<<2], add[maxn<<2], mul[maxn<<2];
 
void Pushup(int rt) {
    sum[rt] = (sum[rs] + sum[ls]) % mod;
}
 
void Build(int l, int r, int rt) {
    if (l == r) {
        ll x; scanf("%lld",&x);
        sum[rt] = x;
        add[rt] = 0;
        mul[rt] = 1;
        return;
    }
    int m = (l + r) >> 1;
    Build(lson);
    Build(rson);
    Pushup(rt);
}
 
void Pushdown(int rt, int len) {
    if (add[rt] != 0 || mul[rt] != 1) {
        add[ls] = (add[ls]*mul[rt] % mod + add[rt]) % mod;
        add[rs] = (add[rs]*mul[rt] % mod + add[rt]) % mod;
         
        mul[ls] = ((mul[ls] % mod) * (mul[rt] % mod)) % mod;
        mul[rs] = ((mul[rs] % mod) * (mul[rt] % mod)) % mod;
         
        sum[ls] = ((sum[ls] % mod) * (mul[rt] % mod)) % mod;
        sum[ls] = (sum[ls] + (len - (len>>1)) * add[rt] % mod) % mod;
         
        sum[rs] = ((sum[rs] % mod) * (mul[rt] % mod)) % mod;
        sum[rs] = (sum[rs] + (len>>1) * add[rt] % mod) % mod;
         
        add[rt] = 0; mul[rt] = 1;
    }
}
 
 
void Add(int L, int R, ll v, int l, int r, int rt) {
    if (L <= l && r <= R) {
        add[rt] = (add[rt] + v) % mod;
        sum[rt] = (sum[rt] + v * (r - l + 1) % mod) % mod;
        return;
    }
    Pushdown(rt, r-l+1);
    int m = (l + r) >> 1;
    if (L <= m) Add(L, R, v, lson);
    if (R > m) Add(L, R, v, rson);
    Pushup(rt);
}
 
void Mul(int L, int R, ll v, int l, int r, int rt) {
    if (L <= l && r <= R) {
        add[rt] = ((add[rt] % mod) * (v % mod)) % mod;
        mul[rt] = ((mul[rt] % mod) * (v % mod)) % mod;
        sum[rt] = ((sum[rt] % mod) * (v % mod)) % mod;
        return;
    }
    Pushdown(rt, r-l+1);
    int m = (l + r) >> 1;
    if (L <= m) Mul(L, R, v, lson);
    if (R > m) Mul(L, R, v, rson);
    Pushup(rt);
}
 
ll Query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) {
        return sum[rt];
    }
    Pushdown(rt, r-l+1);
    ll ret = 0;
    int m = (l + r) >> 1;
    if (L <= m) ret = (ret + Query(L, R, lson)) % mod;
    if (R > m) ret = (ret + Query(L, R, rson)) % mod;
    return ret;
}
 
 
int main() {
    while (scanf("%d%lld",&n,&mod) == 2) {
        Build(1, n, 1);
        int m; scanf("%d",&m);
        memset(add, 0, sizeof(add));
        for (int i = 0; i < maxn<<2; i ++) mul[i] = 1;
//        memset(sum, 0, sizeof(sum));
        while (m--) {
            int op, L, R; scanf("%d%d%d",&op,&L,&R);
            ll v;
            if (op == 1 || op == 2) scanf("%lld",&v);
            if (op == 1) {
                Mul(L, R, v, 1, n, 1);
            } else if (op == 2) {
                Add(L, R, v, 1, n, 1);
            } else {
                printf("%lld\n",Query(L, R, 1, n, 1));
            }
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值