洛谷P3373 【模板】线段树2

这题挺揪心的

想了半天为什么要先乘法后加法

原因是这样的:

1. 加法和乘法顺序不一样会导致不同的结果

比如: (a+b)*c 不等于 a*c + b

而在记录懒标记的时候,加法和乘法两种标记放到一起,并不知道哪个先,哪个后。

所以要确定一个优先级

我们分析一下两种顺序:

(1) 先加后乘 : (a+b)*c = a*c + b*c

(2) 先乘后加:a*c + b

比较一下,发现,上面的先加后乘相当于下面的式子,在加法上面多乘了一个c

所以,我们只要是先加后乘的式子,只要加一个*c就可以转化为先乘后加的式子

具体的操作就是在添加乘法标记的时候,把加法标记*c就好了

所以,我们就定了一个总顺序:先乘后加

 

然后在标记传递pushdown的时候,儿子的加法标记传递完也要保持先乘后加的顺序

比如:addv[o] 是父亲节点的加法标记, mulv[o] 是父亲节点的乘法标记 sumv[o] 是父亲节点的求和值, ls表示左儿子, rs表示右儿子

那么        o

        ls           rs

首先,pushdown 的时候,在算儿子的sumv的时候,ls和rs 的懒标记是不用算的(因为我这里懒标记的定义是:不包含该节点,该节点的儿子加上或乘上的值),所以sumv[ls]和sumv[rs]

还是照样先加后乘的顺序维护

然后,懒标记叠加时候,要把顺序考虑进去

举个例子: a*c+b 这是儿子原来的懒标记

                 然后加入C和B是父亲的懒标记,那么按照先乘后加应该这么算:(a*c+b)*C+B

                            化简:  =   a*c*C+b*C+B

                                 =    a*(c*C) + (b*C+B)

所以,原来的a重叠懒标记后应该是这样的,乘法标记是c*C, 加法标记是 b*C+B

所以,懒标记叠加的时候应该是 mulv 照样乘父亲的mulv, addv要先乘一下父亲的mulv再加一下父亲的addv,这样才算完

 

其他的还是该怎么来就怎么来,这里不细说了

 

完整代码:

  1 #include<cstdio>
  2 #include<iostream>
  3 
  4 #define LL long long int 
  5 #define ls o<<1
  6 #define rs o<<1|1
  7 #define M ((L+R)>>1)
  8 
  9 using namespace std;
 10 const int maxn = 100010;
 11 
 12 LL n, m, p;
 13 LL A[maxn], sumv[maxn*4], addv[maxn*4], mulv[maxn*4];
 14 
 15 LL add(LL a,LL b) {//方便取模 
 16     return (a+b)%p;
 17 }
 18 
 19 LL mul(LL a,LL b) {
 20     return (a*b)%p;
 21 }
 22 
 23 inline void build(LL o,LL L,LL R) {
 24     mulv[o] = 1;
 25     if(L == R) sumv[o] = A[L] % p;
 26     else {
 27         build(ls,L,M);
 28         build(rs,M+1,R);
 29         sumv[o] = add(sumv[ls],sumv[rs]);
 30     }
 31 }
 32 
 33 inline void pushdown(LL o,LL L,LL R) {//划重点 
 34     sumv[ls] = mul(sumv[ls],mulv[o]);
 35     sumv[rs] = mul(sumv[rs],mulv[o]);
 36     sumv[ls] = add(sumv[ls],addv[o]*(M-L+1)); 
 37     sumv[rs] = add(sumv[rs],addv[o]*(R-M));
 38     
 39     mulv[ls] = mul(mulv[ls],mulv[o]);
 40     mulv[rs] = mul(mulv[rs],mulv[o]);
 41     addv[ls] = mul(addv[ls],mulv[o]);
 42     addv[rs] = mul(addv[rs],mulv[o]);
 43     addv[ls] = add(addv[ls],addv[o]);
 44     addv[rs] = add(addv[rs],addv[o]);
 45 
 46     addv[o] = 0;
 47     mulv[o] = 1;     
 48 }
 49 
 50 LL ql,qr,qa,qm;
 51 inline void updatea(LL o,LL L,LL R) {
 52     if(ql <= L &&  R <= qr) {
 53         addv[o] = add(addv[o],qa);
 54         sumv[o] = add(sumv[o],qa*(R-L+1));
 55         return;
 56     }
 57     pushdown(o,L,R);
 58     if(ql <= M) updatea(ls,L,M);
 59     if(qr > M) updatea(rs,M+1,R);
 60   sumv[o] = add(sumv[ls], sumv[rs]);
 61 }
 62 
 63 inline void updatem(LL o,LL L,LL R) {
 64   if(ql <= L && R <= qr) {
 65       mulv[o] = mul(mulv[o],qm);
 66       addv[o] = mul(addv[o],qm);//加法标记*c 
 67       sumv[o] = mul(sumv[o],qm);
 68       return;
 69     }    
 70     pushdown(o,L,R);
 71     if(ql <= M) updatem(ls,L,M);
 72     if(qr > M) updatem(rs,M+1,R);
 73     sumv[o] = add(sumv[ls], sumv[rs]);
 74 }
 75 
 76 inline LL query(LL o,LL L,LL R) {
 77     if(ql <= L && R <= qr) return sumv[o];
 78     pushdown(o,L,R);
 79     LL ret = 0;
 80     if(ql <= M) ret = add(ret, query(ls,L,M));
 81     if(qr > M) ret = add(ret, query(rs,M+1,R));
 82     return ret%p;
 83 }
 84 
 85 int main() {
 86     cin >> n >> m >> p;
 87     for(int i = 1;i <= n;i++) scanf("%lld",&A[i]);
 88 
 89     build(1,1,n);
 90 
 91     for(int i = 1,x;i <= m;i++) {
 92         scanf("%d",&x);
 93         if(x == 1) {
 94             scanf("%lld%lld%lld",&ql,&qr,&qm);
 95             updatem(1,1,n);
 96         } else if(x == 2) {
 97             scanf("%lld%lld%lld",&ql,&qr,&qa);
 98             updatea(1,1,n);
 99         } else {
100             scanf("%lld%lld",&ql,&qr);
101             printf("%lld\n", query(1,1,n));
102         }
103     }
104 
105     return 0;
106 } 

 

posted @ 2018-08-22 21:22  Frank的成长之路  阅读(220)  评论(0编辑  收藏  举报