BZOJ 1798: [Ahoi2009]Seq 维护序列seq

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 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

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。

Solution

线段树裸题,注意需要两个标记:一个mul一个plu,表示当前节点需要先乘mul再加plu,然后合并的时候

像这样搞一搞。sum更新的时候要考虑size大小的。

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
#include <cstdio>
using namespace std;
const int INF = 1<<30;
typedef long long LL;
int getint() {
    int r = 0, k = 1; char c = getchar();
    for (; '0' > c || c > '9'; c = getchar()) if (c == '-') k = -1;
    for (; '0' <= c && c <= '9'; c = getchar()) r = r * 10 - '0' + c;
    return r * k;
}
const int N=100005;
int n, m;
LL p;
LL mul[N*4], plu[N*4], tree[N*4], a[N], siz[N*4];
void pu(int now) {
    tree[now]=(tree[now<<1]+tree[now<<1|1])%p;
}
void build(int now, int l, int r) {
    mul[now]=1;
    plu[now]=0;
    if (l==r) {
        tree[now]=a[l];
        siz[now]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    pu(now);
    siz[now] = siz[now<<1] + siz[now<<1|1];
    return;
}
void pd(int now) {
    if(mul[now]==1&&plu[now]==0) return;
    mul[now<<1]=(mul[now<<1]*mul[now])%p;
    mul[now<<1|1]=(mul[now<<1|1]*mul[now])%p;
    plu[now<<1]=(plu[now<<1]*mul[now]+plu[now])%p;
    plu[now<<1|1]=(plu[now<<1|1]*mul[now]+plu[now])%p;
    tree[now<<1]=(tree[now<<1]*mul[now]+plu[now]*siz[now<<1])%p;
    tree[now<<1|1]=(tree[now<<1|1]*mul[now]+plu[now]*siz[now<<1|1])%p;
    mul[now]=1;plu[now]=0;
}
void chan(int now, int l, int r, int ll, int rr, LL mulval, LL pluval) {
    if (ll<=l&&r<=rr) {
        if (l != r) {
            mul[now]=(mul[now]*mulval)%p;
            plu[now]=(plu[now]*mulval+pluval)%p;
        }
        tree[now]=(tree[now]*mulval+siz[now]*pluval)%p;
        return;
    }
    int mid = (l+r)>>1;
    pd(now);
    if(ll<=mid)chan(now<<1,l,mid,ll,rr,mulval,pluval);
    if(rr>mid)chan(now<<1|1,mid+1,r,ll,rr,mulval,pluval);
    pu(now);
}
LL ask(int now, int l, int r, int ll, int rr) {
    if (ll <= l && r <= rr) return tree[now] % p;
    int mid = (l+r)>>1;
    LL ret = 0;
    pd(now);
    if(ll<=mid)ret=ask(now<<1,l,mid,ll,rr)%p;
    if(rr>mid)ret=(ret+ask(now<<1|1,mid+1,r,ll,rr))%p;
    return ret;
}
int main() {
    n=getint(); p=getint();
    for (int i = 1; i <= n; ++i) a[i]=(getint())%p;
    build(1,1,n);
    int op, x, y, z;
    for (m=getint();m;--m){
        op=getint();
        if (op == 1) {
            x=getint();y=getint();z=getint();
            chan(1,1,n,x,y,z%p,0);
            continue;
        }
        if (op == 2) {
            x=getint();y=getint();z=getint();
            chan(1,1,n,x,y,1,z%p);
            continue;
        }
        if (op == 3) {
            x=getint();y=getint();
            printf("%lld\n", ask(1,1,n,x,y));
            continue;
        }
    }
}