P3373 【模板】线段树 2 (线段树多重标记入门)
题目链接:https://www.luogu.org/problem/P3373
讲讲优先级的问题:
1. 加法和乘法顺序不一样会导致不同的结果
比如: (a+b)c 不等于 ac + b
而在记录懒标记的时候,加法和乘法两种标记放到一起,并不知道哪个先,哪个后。
所以要确定一个优先级
我们分析一下两种顺序:
(1) 先加后乘 : (a+b)c = ac + b*c
(2) 先乘后加:a*c + b
比较一下,发现,上面的先加后乘相当于下面的式子,在加法上面多乘了一个c
所以,我们只要是先加后乘的式子,只要加一个*c就可以转化为先乘后加的式子
具体的操作就是在添加乘法标记的时候,把加法标记*c就好了
所以,我们就定了一个总顺序:先乘后加
然后在标记传递pushdown的时候,儿子的加法标记传递完也要保持先乘后加的顺序
举个例子: ac+b 这是儿子原来的懒标记 然后加入C和B是父亲的懒标记,那么按照先乘后加应该这么算:(ac+b)*C+B
化简: = acC+b*C+B
= a(cC) + (b*C+B)
所以,原来的a重叠懒标记后应该是这样的,乘法标记是cC, 加法标记是 bC+B
1 #include <stdio.h> 2 #include <algorithm> 3 #include <iostream> 4 #include <stdbool.h> 5 #include <stdlib.h> 6 #include <string> 7 #include <string.h> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 #include <math.h> 13 14 #define INF 0x3f3f3f3f 15 #define LL long long 16 using namespace std; 17 18 const int maxn = 300100; 19 LL arr[maxn]; 20 LL p; 21 22 struct segment_tree{ 23 int l,r; 24 LL val; 25 LL mul,add; 26 }tree[maxn*4]; 27 28 void pushup(int nod){ 29 tree[nod].val = (tree[nod<<1].val + tree[(nod<<1)+1].val) % p; 30 } 31 32 void pushdown(int nod){ 33 int l = tree[nod].l,r = tree[nod].r; 34 int mid = (l + r) >> 1; 35 // 我们假设的是乘法优先 36 tree[nod<<1].val = (tree[nod<<1].val*tree[nod].mul + tree[nod].add*(mid-l+1)) % p; 37 tree[(nod<<1)+1].val = (tree[(nod<<1)+1].val*tree[nod].mul + tree[nod].add*(r-mid)) % p; 38 // 维护 mul 和 add 39 tree[nod<<1].mul = (tree[nod<<1].mul*tree[nod].mul) % p; 40 tree[(nod<<1)+1].mul = (tree[(nod<<1)+1].mul*tree[nod].mul) % p; 41 tree[nod<<1].add = (tree[nod<<1].add*tree[nod].mul+tree[nod].add) % p; 42 tree[(nod<<1)+1].add = (tree[(nod<<1)+1].add*tree[nod].mul+tree[nod].add) % p; 43 // 注意清空 44 tree[nod].mul = 1; 45 tree[nod].add = 0; 46 } 47 48 void build(int l,int r,int nod){ 49 tree[nod].mul = 1; 50 tree[nod].add = 0; 51 tree[nod].l = l; 52 tree[nod].r = r; 53 if (l == r){ 54 tree[nod].val = arr[l] % p; 55 return ; 56 } 57 int mid = (l + r) >> 1; 58 build(l,mid,nod<<1); 59 build(mid+1,r,(nod<<1)+1); 60 pushup(nod); 61 } 62 void updata1(int x,int y,LL k,int nod=1){ // 乘法 63 int l = tree[nod].l,r = tree[nod].r; 64 if (x <= l && y >= r){ 65 tree[nod].val = (tree[nod].val * k) % p; 66 tree[nod].mul = (tree[nod].mul * k) % p; 67 // 这里就是我们说的在添加乘法标记的时候,把加法标记*c就好了 68 tree[nod].add = (tree[nod].add * k) % p; 69 return ; 70 } 71 pushdown(nod); 72 int mid = (l + r) >> 1; 73 if (x <= mid){ 74 updata1(x,y,k,nod<<1); 75 } 76 if (y > mid){ 77 updata1(x,y,k,(nod<<1)+1); 78 } 79 pushup(nod); 80 } 81 82 void updata2(int x,int y,LL k,int nod=1){ // 加法 83 int l = tree[nod].l,r = tree[nod].r; 84 if (x <= l && y >= r){ 85 tree[nod].add = (tree[nod].add + k) % p; 86 tree[nod].val = (tree[nod].val + (r-l+1) * k) % p; 87 return ; 88 } 89 pushdown(nod); 90 int mid = (l + r) >> 1; 91 if (x <= mid){ 92 updata2(x,y,k,nod<<1); 93 } 94 if (y > mid){ 95 updata2(x,y,k,(nod<<1)+1); 96 } 97 pushup(nod); 98 } 99 100 LL query(int x,int y,int nod=1){ 101 int l = tree[nod].l,r = tree[nod].r; 102 if (x <= l && y >= r){ 103 return tree[nod].val % p; 104 } 105 pushdown(nod); 106 int mid = (l + r) >> 1; 107 LL sum = 0; 108 if (x <= mid){ 109 sum += query(x,y,nod<<1); 110 } 111 if (y > mid){ 112 sum += query(x,y,(nod<<1)+1); 113 } 114 return sum % p; 115 } 116 117 int main(){ 118 int n,m; 119 scanf("%d%d%lld",&n,&m,&p); 120 for (int i=1;i<=n;i++){ 121 scanf("%lld",&arr[i]); 122 } 123 build(1,n,1); 124 while (m--){ 125 int op; 126 scanf("%d",&op); 127 int x,y; 128 LL k; 129 if (op == 1){ 130 scanf("%d%d%lld",&x,&y,&k); 131 updata1(x,y,k); 132 } 133 else if (op == 2){ 134 scanf("%d%d%lld",&x,&y,&k); 135 updata2(x,y,k); 136 } 137 else { 138 scanf("%d%d",&x,&y); 139 printf("%lld\n",query(x,y)); 140 } 141 } 142 return 0; 143 }