洛谷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 }