区间最值操作 & 区间历史最值 本文讲解吉老师在 2016 年国家集训队论文  中提到的线段树处理历史区间最值的问题。
区间最值 笼统地说,区间最值操作指,将区间 [ 𝑙 , 𝑟 ] [ l , r ] 𝑥 x m a x max m i n min 𝑎 𝑖   = m a x ( 𝑎 𝑖 , 𝑥 ) a i = max ( a i , x ) 𝑎 𝑖   = m i n ( 𝑎 𝑖 , 𝑥 ) a i = min ( a i , x ) 
HDU5306 Gorgeous Sequence 维护一个序列 𝑎 a 
 0 l r t ∀ 𝑙   ≤ 𝑖   ≤ 𝑟 ,   𝑎 𝑖   = m i n ( 𝑎 𝑖 , 𝑡 ) ∀ l ≤ i ≤ r ,   a i = min ( a i , t ) 1 l r 输出 𝑟 m a x 𝑖 = 𝑙 𝑎 𝑖 max i = l r a i 2 l r 输出 𝑟 ∑ 𝑖 = 𝑙 𝑎 𝑖 ∑ i = l r a i 多组测试数据,保证 𝑇   ≤ 1 0 0 ,   ∑ 𝑛 , ∑ 𝑚   ≤ 1 0 6 T ≤ 100 ,   ∑ n , ∑ m ≤ 10 6 
区间取 m i n min 𝑡 t 𝑡 t 𝑀 𝑎 𝑥 M a x 𝑆 𝑒 S e 𝑆 𝑢 𝑚 S u m 𝐶 𝑛 𝑡 C n t 𝑡 t m i n min 
如果 𝑀 𝑎 𝑥   ≤ 𝑡 M a x ≤ t 𝑡 t  如果 𝑆 𝑒   < 𝑡   < 𝑀 𝑎 𝑥 S e < t < M a x 𝑡 t 𝐶 𝑛 𝑡 ( 𝑡   − 𝑀 𝑎 𝑥 ) C n t ( t − M a x ) 𝑀 𝑎 𝑥 M a x 𝑡 t  如果 𝑡   ≤ 𝑆 𝑒 t ≤ S e  这个算法的复杂度如何?使用势能分析法可以得到复杂度是 𝑂 ( 𝑚 l o g  𝑛 ) O ( m log  n ) 
  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 
 92 
 93 
 94 
 95 
 96 
 97 
 98 
 99 
100 
101 
102 
103 #include   <algorithm> 
#include   <cctype> 
#include   <iostream> 
using   namespace   std ; 
constexpr   int   N   =   1e6   +   6 ; 
int   t ,   n ,   m ; 
int   a [ N ]; 
int   mx [ N   <<   2 ],   se [ N   <<   2 ],   cn [ N   <<   2 ],   tag [ N   <<   2 ]; 
long   long   sum [ N   <<   2 ]; 
void   pushup ( int   u )   {    // 向上更新标记 
   const   int   ls   =   u   <<   1 ,   rs   =   u   <<   1   |   1 ; 
   sum [ u ]   =   sum [ ls ]   +   sum [ rs ]; 
   if   ( mx [ ls ]   ==   mx [ rs ])   { 
     mx [ u ]   =   mx [ rs ]; 
     se [ u ]   =   max ( se [ ls ],   se [ rs ]); 
     cn [ u ]   =   cn [ ls ]   +   cn [ rs ]; 
   }   else   if   ( mx [ ls ]   >   mx [ rs ])   { 
     mx [ u ]   =   mx [ ls ]; 
     se [ u ]   =   max ( se [ ls ],   mx [ rs ]); 
     cn [ u ]   =   cn [ ls ]; 
   }   else   { 
     mx [ u ]   =   mx [ rs ]; 
     se [ u ]   =   max ( mx [ ls ],   se [ rs ]); 
     cn [ u ]   =   cn [ rs ]; 
   } 
} 
void   pushtag ( int   u ,   int   tg )   {    // 单纯地打标记,不暴搜 
   if   ( mx [ u ]   <=   tg )   return ; 
   sum [ u ]   +=   ( 1l l   *   tg   -   mx [ u ])   *   cn [ u ]; 
   mx [ u ]   =   tag [ u ]   =   tg ; 
} 
void   pushdown ( int   u )   {    // 下传标记 
   if   ( tag [ u ]   ==   -1 )   return ; 
   pushtag ( u   <<   1 ,   tag [ u ]),   pushtag ( u   <<   1   |   1 ,   tag [ u ]); 
   tag [ u ]   =   -1 ; 
} 
void   build ( int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   {    // 建树 
   tag [ u ]   =   -1 ; 
   if   ( l   ==   r )   { 
     sum [ u ]   =   mx [ u ]   =   a [ l ],   se [ u ]   =   -1 ,   cn [ u ]   =   1 ; 
     return ; 
   } 
   int   mid   =   ( l   +   r )   >>   1 ; 
   build ( u   <<   1 ,   l ,   mid ),   build ( u   <<   1   |   1 ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
void   modify_min ( int   L ,   int   R ,   int   v ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( mx [ u ]   <=   v )   return ; 
   if   ( L   <=   l   &&   r   <=   R   &&   se [ u ]   <   v )   return   pushtag ( u ,   v ); 
   int   mid   =   ( l   +   r )   >>   1 ; 
   pushdown ( u ); 
   if   ( L   <=   mid )   modify_min ( L ,   R ,   v ,   u   <<   1 ,   l ,   mid ); 
   if   ( mid   <   R )   modify_min ( L ,   R ,   v ,   u   <<   1   |   1 ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
int   query_max ( int   L ,   int   R ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   {    // 查询最值 
   if   ( L   <=   l   &&   r   <=   R )   return   mx [ u ]; 
   int   mid   =   ( l   +   r )   >>   1 ,   r1   =   -1 ,   r2   =   -1 ; 
   pushdown ( u ); 
   if   ( L   <=   mid )   r1   =   query_max ( L ,   R ,   u   <<   1 ,   l ,   mid ); 
   if   ( mid   <   R )   r2   =   query_max ( L ,   R ,   u   <<   1   |   1 ,   mid   +   1 ,   r ); 
   return   max ( r1 ,   r2 ); 
} 
long   long   query_sum ( int   L ,   int   R ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   {    // 数值 
   if   ( L   <=   l   &&   r   <=   R )   return   sum [ u ]; 
   int   mid   =   ( l   +   r )   >>   1 ; 
   long   long   res   =   0 ; 
   pushdown ( u ); 
   if   ( L   <=   mid )   res   +=   query_sum ( L ,   R ,   u   <<   1 ,   l ,   mid ); 
   if   ( mid   <   R )   res   +=   query_sum ( L ,   R ,   u   <<   1   |   1 ,   mid   +   1 ,   r ); 
   return   res ; 
} 
void   go ()   {    // 根据题意 
   cin   >>   n   >>   m ; 
   for   ( int   i   =   1 ;   i   <=   n ;   i ++ )   cin   >>   a [ i ]; 
   build (); 
   for   ( int   i   =   1 ;   i   <=   m ;   i ++ )   { 
     int   op ,   x ,   y ,   z ; 
     cin   >>   op   >>   x   >>   y ; 
     if   ( op   ==   0 ) 
       cin   >>   z ,   modify_min ( x ,   y ,   z ); 
     else   if   ( op   ==   1 ) 
       cout   <<   query_max ( x ,   y )   <<   '\n' ; 
     else 
       cout   <<   query_sum ( x ,   y )   <<   '\n' ; 
   } 
} 
signed   main ()   { 
   cin . tie ( nullptr ) -> sync_with_stdio ( false ); 
   cin   >>   t ; 
   while   ( t -- )   go (); 
   return   0 ; 
} 
BZOJ4695 最假女选手 维护一个序列 𝑎 a 
 1 l r x ∀ 𝑙   ≤ 𝑖   ≤ 𝑟 ,   𝑎 𝑖   = 𝑎 𝑖   + 𝑥 ∀ l ≤ i ≤ r ,   a i = a i + x 2 l r x ∀ 𝑙   ≤ 𝑖   ≤ 𝑟 ,   𝑎 𝑖   = m a x ( 𝑎 𝑖 , 𝑥 ) ∀ l ≤ i ≤ r ,   a i = max ( a i , x ) 3 l r x ∀ 𝑙   ≤ 𝑖   ≤ 𝑟 ,   𝑎 𝑖   = m i n ( 𝑎 𝑖 , 𝑥 ) ∀ l ≤ i ≤ r ,   a i = min ( a i , x ) 4 l r 输出 𝑟 ∑ 𝑖 = 𝑙 𝑎 𝑖 ∑ i = l r a i 5 l r 输出 𝑟 m a x 𝑖 = 𝑙 𝑎 𝑖 max i = l r a i 6 l r 输出 𝑟 m i n 𝑖 = 𝑙 𝑎 𝑖 min i = l r a i 𝑛 , 𝑚   ≤ 5   × 1 0 5 ,   | 𝑎 𝑖 |   ≤ 1 0 8 n , m ≤ 5 × 10 5 ,   | a i | ≤ 10 8 1 1 | 𝑥 |   ≤ 1 0 3 | x | ≤ 10 3 | 𝑥 |   ≤ 1 0 8 | x | ≤ 10 8 
同样的方法,我们维护最大、次大、最大个数、最小、次小、最小个数、区间和。除了这些信息,我们还需要维护区间 m a x max m i n min 
我们认为区间加的标记是最优先的,其余两种标记地位平等。 对一个结点加上一个 𝑣 v 𝑣 v m a x max m i n min  对一个结点取 𝑣 v m i n min m a x max 𝑣 v m a x max m a x max 𝑣 v  区间取 v 的 m a x max  在维护信息的时侯,当只有一个数或两个数的时侯可能发生数集重合,比如一个数既是最大值又是次小值,需要特判。
  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 
 92 
 93 
 94 
 95 
 96 
 97 
 98 
 99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160 
161 
162 
163 
164 
165 
166 #include   <iostream> 
using   namespace   std ; 
constexpr   int   N   =   5e5   +   5 ,   SZ   =   N   <<   2 ,   INF   =   0x7fffffff ; 
int   n ,   m ; 
int   a [ N ]; 
struct   data   { 
   int   mx ,   mx2 ,   mn ,   mn2 ,   cmx ,   cmn ,   tmx ,   tmn ,   tad ; 
   long   long   sum ; 
}   t [ SZ ]; 
void   pushup ( int   u )   { 
   const   int   lu   =   u   <<   1 ,   ru   =   u   <<   1   |   1 ; 
   t [ u ]. sum   =   t [ lu ]. sum   +   t [ ru ]. sum ; 
   if   ( t [ lu ]. mx   ==   t [ ru ]. mx )   { 
     t [ u ]. mx   =   t [ lu ]. mx ,   t [ u ]. cmx   =   t [ lu ]. cmx   +   t [ ru ]. cmx ; 
     t [ u ]. mx2   =   max ( t [ lu ]. mx2 ,   t [ ru ]. mx2 ); 
   }   else   if   ( t [ lu ]. mx   >   t [ ru ]. mx )   { 
     t [ u ]. mx   =   t [ lu ]. mx ,   t [ u ]. cmx   =   t [ lu ]. cmx ; 
     t [ u ]. mx2   =   max ( t [ lu ]. mx2 ,   t [ ru ]. mx ); 
   }   else   { 
     t [ u ]. mx   =   t [ ru ]. mx ,   t [ u ]. cmx   =   t [ ru ]. cmx ; 
     t [ u ]. mx2   =   max ( t [ lu ]. mx ,   t [ ru ]. mx2 ); 
   } 
   if   ( t [ lu ]. mn   ==   t [ ru ]. mn )   { 
     t [ u ]. mn   =   t [ lu ]. mn ,   t [ u ]. cmn   =   t [ lu ]. cmn   +   t [ ru ]. cmn ; 
     t [ u ]. mn2   =   min ( t [ lu ]. mn2 ,   t [ ru ]. mn2 ); 
   }   else   if   ( t [ lu ]. mn   <   t [ ru ]. mn )   { 
     t [ u ]. mn   =   t [ lu ]. mn ,   t [ u ]. cmn   =   t [ lu ]. cmn ; 
     t [ u ]. mn2   =   min ( t [ lu ]. mn2 ,   t [ ru ]. mn ); 
   }   else   { 
     t [ u ]. mn   =   t [ ru ]. mn ,   t [ u ]. cmn   =   t [ ru ]. cmn ; 
     t [ u ]. mn2   =   min ( t [ lu ]. mn ,   t [ ru ]. mn2 ); 
   } 
} 
void   push_add ( int   u ,   int   l ,   int   r ,   int   v )   { 
   // 更新加法标记的同时,更新 $\min$ 和 $\max$ 标记 
   t [ u ]. sum   +=   ( r   -   l   +   1l l )   *   v ; 
   t [ u ]. mx   +=   v ,   t [ u ]. mn   +=   v ; 
   if   ( t [ u ]. mx2   !=   - INF )   t [ u ]. mx2   +=   v ; 
   if   ( t [ u ]. mn2   !=   INF )   t [ u ]. mn2   +=   v ; 
   if   ( t [ u ]. tmx   !=   - INF )   t [ u ]. tmx   +=   v ; 
   if   ( t [ u ]. tmn   !=   INF )   t [ u ]. tmn   +=   v ; 
   t [ u ]. tad   +=   v ; 
} 
void   push_min ( int   u ,   int   tg )   { 
   // 注意比较 $\max$ 标记 
   if   ( t [ u ]. mx   <=   tg )   return ; 
   t [ u ]. sum   +=   ( tg   *   1l l   -   t [ u ]. mx )   *   t [ u ]. cmx ; 
   if   ( t [ u ]. mn2   ==   t [ u ]. mx )   t [ u ]. mn2   =   tg ;    // !!! 
   if   ( t [ u ]. mn   ==   t [ u ]. mx )   t [ u ]. mn   =   tg ;      // !!!!! 
   if   ( t [ u ]. tmx   >   tg )   t [ u ]. tmx   =   tg ;          // 更新取 $\max$ 标记 
   t [ u ]. mx   =   tg ,   t [ u ]. tmn   =   tg ; 
} 
void   push_max ( int   u ,   int   tg )   { 
   if   ( t [ u ]. mn   >   tg )   return ; 
   t [ u ]. sum   +=   ( tg   *   1l l   -   t [ u ]. mn )   *   t [ u ]. cmn ; 
   if   ( t [ u ]. mx2   ==   t [ u ]. mn )   t [ u ]. mx2   =   tg ; 
   if   ( t [ u ]. mx   ==   t [ u ]. mn )   t [ u ]. mx   =   tg ; 
   if   ( t [ u ]. tmn   <   tg )   t [ u ]. tmn   =   tg ; 
   t [ u ]. mn   =   tg ,   t [ u ]. tmx   =   tg ; 
} 
void   pushdown ( int   u ,   int   l ,   int   r )   { 
   const   int   lu   =   u   <<   1 ,   ru   =   u   <<   1   |   1 ,   mid   =   ( l   +   r )   >>   1 ; 
   if   ( t [ u ]. tad ) 
     push_add ( lu ,   l ,   mid ,   t [ u ]. tad ),   push_add ( ru ,   mid   +   1 ,   r ,   t [ u ]. tad ); 
   if   ( t [ u ]. tmx   !=   - INF )   push_max ( lu ,   t [ u ]. tmx ),   push_max ( ru ,   t [ u ]. tmx ); 
   if   ( t [ u ]. tmn   !=   INF )   push_min ( lu ,   t [ u ]. tmn ),   push_min ( ru ,   t [ u ]. tmn ); 
   t [ u ]. tad   =   0 ,   t [ u ]. tmx   =   - INF ,   t [ u ]. tmn   =   INF ; 
} 
void   build ( int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   t [ u ]. tmn   =   INF ,   t [ u ]. tmx   =   - INF ;    // 取极限 
   if   ( l   ==   r )   { 
     t [ u ]. sum   =   t [ u ]. mx   =   t [ u ]. mn   =   a [ l ]; 
     t [ u ]. mx2   =   - INF ,   t [ u ]. mn2   =   INF ; 
     t [ u ]. cmx   =   t [ u ]. cmn   =   1 ; 
     return ; 
   } 
   int   mid   =   ( l   +   r )   >>   1 ; 
   build ( u   <<   1 ,   l ,   mid ),   build ( u   <<   1   |   1 ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
void   add ( int   L ,   int   R ,   int   v ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L )   return ; 
   if   ( L   <=   l   &&   r   <=   R )   return   push_add ( u ,   l ,   r ,   v );    // !!! 忘 return 
   int   mid   =   ( l   +   r )   >>   1 ; 
   pushdown ( u ,   l ,   r ); 
   add ( L ,   R ,   v ,   u   <<   1 ,   l ,   mid ),   add ( L ,   R ,   v ,   u   <<   1   |   1 ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
void   tomin ( int   L ,   int   R ,   int   v ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L   ||   t [ u ]. mx   <=   v )   return ; 
   if   ( L   <=   l   &&   r   <=   R   &&   t [ u ]. mx2   <   v )   return   push_min ( u ,   v );    // BUG: 忘了返回 
   int   mid   =   ( l   +   r )   >>   1 ; 
   pushdown ( u ,   l ,   r ); 
   tomin ( L ,   R ,   v ,   u   <<   1 ,   l ,   mid ),   tomin ( L ,   R ,   v ,   u   <<   1   |   1 ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
void   tomax ( int   L ,   int   R ,   int   v ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L   ||   t [ u ]. mn   >=   v )   return ; 
   if   ( L   <=   l   &&   r   <=   R   &&   t [ u ]. mn2   >   v )   return   push_max ( u ,   v ); 
   int   mid   =   ( l   +   r )   >>   1 ; 
   pushdown ( u ,   l ,   r ); 
   tomax ( L ,   R ,   v ,   u   <<   1 ,   l ,   mid ),   tomax ( L ,   R ,   v ,   u   <<   1   |   1 ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
long   long   qsum ( int   L ,   int   R ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L )   return   0 ; 
   if   ( L   <=   l   &&   r   <=   R )   return   t [ u ]. sum ; 
   int   mid   =   ( l   +   r )   >>   1 ; 
   pushdown ( u ,   l ,   r ); 
   return   qsum ( L ,   R ,   u   <<   1 ,   l ,   mid )   +   qsum ( L ,   R ,   u   <<   1   |   1 ,   mid   +   1 ,   r ); 
} 
long   long   qmax ( int   L ,   int   R ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L )   return   - INF ; 
   if   ( L   <=   l   &&   r   <=   R )   return   t [ u ]. mx ; 
   int   mid   =   ( l   +   r )   >>   1 ; 
   pushdown ( u ,   l ,   r ); 
   return   max ( qmax ( L ,   R ,   u   <<   1 ,   l ,   mid ),   qmax ( L ,   R ,   u   <<   1   |   1 ,   mid   +   1 ,   r )); 
} 
long   long   qmin ( int   L ,   int   R ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L )   return   INF ; 
   if   ( L   <=   l   &&   r   <=   R )   return   t [ u ]. mn ; 
   int   mid   =   ( l   +   r )   >>   1 ; 
   pushdown ( u ,   l ,   r ); 
   return   min ( qmin ( L ,   R ,   u   <<   1 ,   l ,   mid ),   qmin ( L ,   R ,   u   <<   1   |   1 ,   mid   +   1 ,   r )); 
} 
int   main ()   { 
   cin . tie ( nullptr ) -> sync_with_stdio ( false ); 
   cin   >>   n ; 
   for   ( int   i   =   1 ;   i   <=   n ;   i ++ )   cin   >>   a [ i ]; 
   build (); 
   cin   >>   m ; 
   for   ( int   i   =   1 ;   i   <=   m ;   i ++ )   { 
     int   op ,   l ,   r ,   x ; 
     cin   >>   op   >>   l   >>   r ; 
     if   ( op   <=   3 )   cin   >>   x ; 
     if   ( op   ==   1 ) 
       add ( l ,   r ,   x ); 
     else   if   ( op   ==   2 ) 
       tomax ( l ,   r ,   x ); 
     else   if   ( op   ==   3 ) 
       tomin ( l ,   r ,   x ); 
     else   if   ( op   ==   4 ) 
       cout   <<   qsum ( l ,   r )   <<   '\n' ; 
     else   if   ( op   ==   5 ) 
       cout   <<   qmax ( l ,   r )   <<   '\n' ; 
     else 
       cout   <<   qmin ( l ,   r )   <<   '\n' ; 
   } 
   return   0 ; 
} 
吉老师证出来这个算法的复杂度是 𝑂 ( 𝑚 l o g 2  𝑛 ) O ( m log 2  n ) 
Mzl loves segment tree  两个序列 𝐴 , 𝐵 A , B 𝐵 B 0 0 
 对 𝐴 A m i n min  对 𝐴 A m a x max  对 𝐴 A  询问 𝐵 B  每次操作完后,如果 𝐴 𝑖 A i 𝐵 𝑖 B i 1 1 𝑛 , 𝑚   ≤ 3   × 1 0 5 n , m ≤ 3 × 10 5 
先考虑最容易的区间加操作。只要 𝑥   ≠ 0 x ≠ 0 
对于区间取最值的操作,你发现你打标记与下传标记是与 𝐵 B 𝐵 B 𝐵 B 𝐴 A 𝐵 B 𝐵 B 𝐵 B 
CTSN loves segment tree 维护两个序列 𝑎 , 𝑏 a , b 
 1 l r x ∀ 𝑙   ≤ 𝑖   ≤ 𝑟 ,   𝑎 𝑖   = m i n ( 𝑎 𝑖 , 𝑥 ) ∀ l ≤ i ≤ r ,   a i = min ( a i , x ) 2 l r x ∀ 𝑙   ≤ 𝑖   ≤ 𝑟 ,   𝑏 𝑖   = m i n ( 𝑏 𝑖 , 𝑥 ) ∀ l ≤ i ≤ r ,   b i = min ( b i , x ) 3 l r x ∀ 𝑙   ≤ 𝑖   ≤ 𝑟 ,   𝑎 𝑖   = 𝑎 𝑖   + 𝑥 ∀ l ≤ i ≤ r ,   a i = a i + x 4 l r x ∀ 𝑙   ≤ 𝑖   ≤ 𝑟 ,   𝑏 𝑖   = 𝑏 𝑖   + 𝑥 ∀ l ≤ i ≤ r ,   b i = b i + x 5 l r 输出 𝑟 m a x 𝑖 = 𝑙 ( 𝑎 𝑖   + 𝑏 𝑖 ) max i = l r ( a i + b i ) 𝑛 , 𝑚   ≤ 3   × 1 0 5 ,   | 𝑎 𝑖 | , | 𝑏 𝑖 | , | 𝑥 |   ≤ 1 0 9 n , m ≤ 3 × 10 5 ,   | a i | , | b i | , | x | ≤ 10 9 
我们把区间 [ 𝑙 , 𝑟 ] [ l , r ] 𝐴 𝑖   + 𝐵 𝑖 A i + B i 𝐴 𝑖 , 𝐵 𝑖 A i , B i 𝐴 , 𝐵 A , B 𝐴 𝑖 A i 𝐴 A 𝐵 𝑖 B i 𝐵 B 𝐴 𝑖 A i 𝐴 A 𝐵 𝑖 B i 𝐵 B 𝐴 𝑖 , 𝐵 𝑖 A i , B i 𝐴 , 𝐵 A , B 𝐶 0 , 0 , 𝐶 1 , 0 , 𝐶 0 , 1 , 𝐶 1 , 1 C 0 , 0 , C 1 , 0 , C 0 , 1 , C 1 , 1 𝐴 , 𝐵 A , B m i n min 𝐴 , 𝐵 A , B 𝐴 A m i n min 𝐶 1 , 1 C 1 , 1 𝐶 1 , 0 C 1 , 0 𝐵 B 𝐶 1 , 1 C 1 , 1 𝐶 0 , 1 C 0 , 1 𝐴 , 𝐵 A , B 𝐶 0 , 0 , 𝐶 1 , 0 , 𝐶 0 , 1 , 𝐶 1 , 1 C 0 , 0 , C 1 , 0 , C 0 , 1 , C 1 , 1 𝐶 0 , 0 , 𝐶 1 , 0 , 𝐶 0 , 1 C 0 , 0 , C 1 , 0 , C 0 , 1 [ 𝑖 , 𝑖 ] [ i , i ] 𝐴 , 𝐵 A , B 𝐶 1 , 1 C 1 , 1 
接下来需要考虑在 pushup 时如何维护 𝐶 0 , 0 , 𝐶 1 , 0 , 𝐶 0 , 1 , 𝐶 1 , 1 C 0 , 0 , C 1 , 0 , C 0 , 1 , C 1 , 1 𝐴 , 𝐵 A , B 𝐴 , 𝐵 A , B 𝐴 , 𝐵 A , B 
当左儿子的 𝐴 , 𝐵 A , B 𝐴 , 𝐵 A , B 𝐶 0 , 0 , 𝐶 1 , 0 , 𝐶 0 , 1 , 𝐶 1 , 1 C 0 , 0 , C 1 , 0 , C 0 , 1 , C 1 , 1 𝐶 0 , 0 , 𝐶 1 , 0 , 𝐶 0 , 1 , 𝐶 1 , 1 C 0 , 0 , C 1 , 0 , C 0 , 1 , C 1 , 1  当左儿子的 𝐴 A 𝐴 A 𝐵 B 𝐶 1 , 0 , 𝐶 1 , 1 C 1 , 0 , C 1 , 1 𝐶 1 , 0 C 1 , 0 𝐶 0 , 0 , 𝐶 0 , 1 C 0 , 0 , C 0 , 1 𝐶 0 , 0 C 0 , 0  当左儿子的 𝐴 A 𝐴 A 𝐵 B 𝐶 0 , 1 , 𝐶 1 , 1 C 0 , 1 , C 1 , 1 𝐶 0 , 1 C 0 , 1 𝐶 0 , 0 , 𝐶 1 , 0 C 0 , 0 , C 1 , 0 𝐶 0 , 0 C 0 , 0  当左儿子的 𝐴 , 𝐵 A , B 𝐴 , 𝐵 A , B 𝐶 0 , 0 , 𝐶 1 , 0 , 𝐶 0 , 1 , 𝐶 1 , 1 C 0 , 0 , C 1 , 0 , C 0 , 1 , C 1 , 1 𝐶 0 , 0 C 0 , 0  区间查询结果的 m a x ( 𝐶 0 , 0 , 𝐶 1 , 0 , 𝐶 0 , 1 , 𝐶 1 , 1 ) max ( C 0 , 0 , C 1 , 0 , C 0 , 1 , C 1 , 1 ) 
由于需要同时维护区间 m i n min 𝑂 ( 𝑚 l o g 2  𝑛 ) O ( m log 2  n ) 
  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 
 92 
 93 
 94 
 95 
 96 
 97 
 98 
 99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160 
161 
162 
163 
164 
165 
166 
167 
168 
169 
170 
171 
172 
173 
174 
175 
176 
177 
178 
179 
180 
181 
182 
183 
184 
185 
186 
187 
188 
189 #include   <algorithm> 
#include   <cstdint> 
#include   <iostream> 
using   i64   =   int64_t ; 
constexpr   int   N   =   300010 ; 
constexpr   i64   INF   =   1145141919810114514l l ; 
int   n ,   q ,   op ,   l ,   r ; 
int   a [ N ],   b [ N ]; 
int   lc ( int   u )   {   return   ( u   <<   1 );   } 
int   rc ( int   u )   {   return   ( u   <<   1 )   |   1 ;   } 
struct   node   { 
   i64   mx_ab [ 2 ][ 2 ];    // 0: not max A or B; 1: is max A or B 
   i64   max_val ()   { 
     return   std :: max ({ mx_ab [ 0 ][ 0 ],   mx_ab [ 0 ][ 1 ],   mx_ab [ 1 ][ 0 ],   mx_ab [ 1 ][ 1 ]}); 
   } 
   i64   mx1 [ 2 ],   mx2 [ 2 ];    // 0: A; 1: B 
   i64   tag_add [ 2 ],   tag_mn [ 2 ]; 
   void   clr_tag ()   {   tag_add [ 0 ]   =   tag_add [ 1 ]   =   0 ,   tag_mn [ 0 ]   =   tag_mn [ 1 ]   =   INF ;   } 
   void   init ( i64   a ,   i64   b )   { 
     mx1 [ 0 ]   =   a ,   mx1 [ 1 ]   =   b ,   mx2 [ 0 ]   =   mx2 [ 1 ]   =   - INF ; 
     mx_ab [ 1 ][ 1 ]   =   a   +   b ,   mx_ab [ 0 ][ 1 ]   =   mx_ab [ 1 ][ 0 ]   =   mx_ab [ 0 ][ 0 ]   =   - INF ; 
   } 
   void   modify_mn ( i64   tg ,   bool   id )    // id 0: A; 1: B 
   { 
     if   ( mx1 [ id ]   <=   tg )   return ; 
     mx_ab [ 1 ][ 1 ]   +=   ( mx_ab [ 1 ][ 1 ]   >   - INF )   ?   ( tg   -   mx1 [ id ])   :   0 ; 
     mx_ab [ ! id ][ id ]   +=   ( mx_ab [ ! id ][ id ]   >   - INF )   ?   ( tg   -   mx1 [ id ])   :   0 ; 
     mx1 [ id ]   =   tag_mn [ id ]   =   tg ; 
   } 
   void   modify_add ( i64   tg ,   bool   id )    // id 0: A; 1: B 
   { 
     for   ( int   i   =   0 ;   i   <   2 ;   ++ i ) 
       for   ( int   j   =   0 ;   j   <   2 ;   ++ j )   mx_ab [ i ][ j ]   +=   ( mx_ab [ i ][ j ]   >   - INF )   ?   tg   :   0 ; 
     mx1 [ id ]   +=   tg ,   mx2 [ id ]   +=   ( mx2 [ id ]   >   - INF )   ?   tg   :   0 ; 
     tag_mn [ id ]   +=   ( tag_mn [ id ]   <   INF )   ?   tg   :   0 ,   tag_add [ id ]   +=   tg ; 
   } 
   void   pushup ( const   node &   l ,   const   node &   r )   { 
     for   ( int   i   =   0 ;   i   <   2 ;   ++ i )   { 
       if   ( l . mx1 [ i ]   ==   r . mx1 [ i ]) 
         mx1 [ i ]   =   l . mx1 [ i ],   mx2 [ i ]   =   std :: max ( l . mx2 [ i ],   r . mx2 [ i ]); 
       else   if   ( l . mx1 [ i ]   >   r . mx1 [ i ]) 
         mx1 [ i ]   =   l . mx1 [ i ],   mx2 [ i ]   =   std :: max ( l . mx2 [ i ],   r . mx1 [ i ]); 
       else 
         mx1 [ i ]   =   r . mx1 [ i ],   mx2 [ i ]   =   std :: max ( l . mx1 [ i ],   r . mx2 [ i ]); 
     } 
     if   ( l . mx1 [ 0 ]   ==   mx1 [ 0 ]   &&   l . mx1 [ 1 ]   ==   mx1 [ 1 ])   { 
       mx_ab [ 1 ][ 1 ]   =   l . mx_ab [ 1 ][ 1 ],   mx_ab [ 0 ][ 1 ]   =   l . mx_ab [ 0 ][ 1 ]; 
       mx_ab [ 1 ][ 0 ]   =   l . mx_ab [ 1 ][ 0 ],   mx_ab [ 0 ][ 0 ]   =   l . mx_ab [ 0 ][ 0 ]; 
     }   else   if   ( l . mx1 [ 0 ]   ==   mx1 [ 0 ]   &&   l . mx1 [ 1 ]   !=   mx1 [ 1 ])   { 
       mx_ab [ 1 ][ 1 ]   =   mx_ab [ 0 ][ 1 ]   =   - INF ; 
       mx_ab [ 1 ][ 0 ]   =   std :: max ( l . mx_ab [ 1 ][ 1 ],   l . mx_ab [ 1 ][ 0 ]); 
       mx_ab [ 0 ][ 0 ]   =   std :: max ( l . mx_ab [ 0 ][ 1 ],   l . mx_ab [ 0 ][ 0 ]); 
     }   else   if   ( l . mx1 [ 0 ]   !=   mx1 [ 0 ]   &&   l . mx1 [ 1 ]   ==   mx1 [ 1 ])   { 
       mx_ab [ 1 ][ 1 ]   =   mx_ab [ 1 ][ 0 ]   =   - INF ; 
       mx_ab [ 0 ][ 1 ]   =   std :: max ( l . mx_ab [ 1 ][ 1 ],   l . mx_ab [ 0 ][ 1 ]); 
       mx_ab [ 0 ][ 0 ]   =   std :: max ( l . mx_ab [ 1 ][ 0 ],   l . mx_ab [ 0 ][ 0 ]); 
     }   else   { 
       mx_ab [ 1 ][ 1 ]   =   mx_ab [ 0 ][ 1 ]   =   mx_ab [ 1 ][ 0 ]   =   - INF ; 
       mx_ab [ 0 ][ 0 ]   =   std :: max ( 
           { l . mx_ab [ 0 ][ 0 ],   l . mx_ab [ 0 ][ 1 ],   l . mx_ab [ 1 ][ 0 ],   l . mx_ab [ 1 ][ 1 ]}); 
     } 
     if   ( r . mx1 [ 0 ]   ==   mx1 [ 0 ]   &&   r . mx1 [ 1 ]   ==   mx1 [ 1 ])   { 
       mx_ab [ 1 ][ 1 ]   =   std :: max ( mx_ab [ 1 ][ 1 ],   r . mx_ab [ 1 ][ 1 ]); 
       mx_ab [ 0 ][ 1 ]   =   std :: max ( mx_ab [ 0 ][ 1 ],   r . mx_ab [ 0 ][ 1 ]); 
       mx_ab [ 1 ][ 0 ]   =   std :: max ( mx_ab [ 1 ][ 0 ],   r . mx_ab [ 1 ][ 0 ]); 
       mx_ab [ 0 ][ 0 ]   =   std :: max ( mx_ab [ 0 ][ 0 ],   r . mx_ab [ 0 ][ 0 ]); 
     }   else   if   ( r . mx1 [ 0 ]   ==   mx1 [ 0 ]   &&   r . mx1 [ 1 ]   !=   mx1 [ 1 ])   { 
       mx_ab [ 1 ][ 0 ]   =   std :: max ({ mx_ab [ 1 ][ 0 ],   r . mx_ab [ 1 ][ 1 ],   r . mx_ab [ 1 ][ 0 ]}); 
       mx_ab [ 0 ][ 0 ]   =   std :: max ({ mx_ab [ 0 ][ 0 ],   r . mx_ab [ 0 ][ 1 ],   r . mx_ab [ 0 ][ 0 ]}); 
     }   else   if   ( r . mx1 [ 0 ]   !=   mx1 [ 0 ]   &&   r . mx1 [ 1 ]   ==   mx1 [ 1 ])   { 
       mx_ab [ 0 ][ 1 ]   =   std :: max ({ mx_ab [ 0 ][ 1 ],   r . mx_ab [ 1 ][ 1 ],   r . mx_ab [ 0 ][ 1 ]}); 
       mx_ab [ 0 ][ 0 ]   =   std :: max ({ mx_ab [ 0 ][ 0 ],   r . mx_ab [ 1 ][ 0 ],   r . mx_ab [ 0 ][ 0 ]}); 
     }   else 
       mx_ab [ 0 ][ 0 ]   =   std :: max ({ mx_ab [ 0 ][ 0 ],   r . mx_ab [ 0 ][ 0 ],   r . mx_ab [ 0 ][ 1 ], 
                               r . mx_ab [ 1 ][ 0 ],   r . mx_ab [ 1 ][ 1 ]}); 
   } 
}   tr [ N   <<   2 ]; 
void   pushup ( int   u )   {   tr [ u ]. pushup ( tr [ lc ( u )],   tr [ rc ( u )]);   } 
void   pushdown ( int   u )   { 
   for   ( int   i   =   0 ;   i   <   2 ;   ++ i ) 
     if   ( tr [ u ]. tag_add [ i ]) 
       tr [ lc ( u )]. modify_add ( tr [ u ]. tag_add [ i ],   i ), 
           tr [ rc ( u )]. modify_add ( tr [ u ]. tag_add [ i ],   i ); 
   for   ( int   i   =   0 ;   i   <   2 ;   ++ i ) 
     if   ( tr [ u ]. tag_mn [ i ]   <   INF ) 
       tr [ lc ( u )]. modify_mn ( tr [ u ]. tag_mn [ i ],   i ), 
           tr [ rc ( u )]. modify_mn ( tr [ u ]. tag_mn [ i ],   i ); 
   tr [ u ]. clr_tag (); 
} 
void   _build ( int   u ,   int   L ,   int   R )   { 
   tr [ u ]. clr_tag (); 
   if   ( L   ==   R )   return   tr [ u ]. init ( a [ L ],   b [ L ]); 
   int   M   =   ( L   +   R )   >>   1 ; 
   _build ( lc ( u ),   L ,   M ),   _build ( rc ( u ),   M   +   1 ,   R ),   pushup ( u ); 
} 
void   build ()   {   _build ( 1 ,   1 ,   n );   } 
void   _modify_mn ( int   u ,   int   l ,   int   r ,   int   L ,   int   R ,   int   v ,   bool   i )   { 
   if   ( L   >   r   ||   R   <   l   ||   tr [ u ]. mx1 [ i ]   <=   v )   return ; 
   if   ( l   <=   L   &&   R   <=   r   &&   tr [ u ]. mx2 [ i ]   <   v )   return   tr [ u ]. modify_mn ( v ,   i ); 
   pushdown ( u ); 
   int   M   =   ( L   +   R )   >>   1 ; 
   _modify_mn ( lc ( u ),   l ,   r ,   L ,   M ,   v ,   i ),   _modify_mn ( rc ( u ),   l ,   r ,   M   +   1 ,   R ,   v ,   i ); 
   pushup ( u ); 
} 
void   modify_mn ( int   l ,   int   r ,   int   v ,   bool   i )   {   _modify_mn ( 1 ,   l ,   r ,   1 ,   n ,   v ,   i );   } 
void   _modify_add ( int   u ,   int   l ,   int   r ,   int   L ,   int   R ,   int   v ,   bool   i )   { 
   if   ( L   >   r   ||   R   <   l )   return ; 
   if   ( l   <=   L   &&   R   <=   r )   return   tr [ u ]. modify_add ( v ,   i ); 
   pushdown ( u ); 
   int   M   =   ( L   +   R )   >>   1 ; 
   _modify_add ( lc ( u ),   l ,   r ,   L ,   M ,   v ,   i ), 
       _modify_add ( rc ( u ),   l ,   r ,   M   +   1 ,   R ,   v ,   i ); 
   pushup ( u ); 
} 
void   modify_add ( int   l ,   int   r ,   int   v ,   bool   i )   { 
   _modify_add ( 1 ,   l ,   r ,   1 ,   n ,   v ,   i ); 
} 
node   _query ( int   u ,   int   l ,   int   r ,   int   L ,   int   R )   { 
   if   ( l   <=   L   &&   R   <=   r )   return   tr [ u ]; 
   pushdown ( u ); 
   int   M   =   ( L   +   R )   >>   1 ; 
   if   ( r   <=   M )   return   _query ( lc ( u ),   l ,   r ,   L ,   M ); 
   if   ( l   >   M )   return   _query ( rc ( u ),   l ,   r ,   M   +   1 ,   R ); 
   node   ret ; 
   node   _l   =   _query ( lc ( u ),   l ,   M ,   L ,   M ),   _r   =   _query ( rc ( u ),   M   +   1 ,   r ,   M   +   1 ,   R ); 
   ret . pushup ( _l ,   _r ); 
   return   ret ; 
} 
node   query ( int   l ,   int   r )   {   return   _query ( 1 ,   l ,   r ,   1 ,   n );   } 
using   std :: cin ; 
using   std :: cout ; 
int   main ()   { 
   cin . tie ( nullptr ) -> sync_with_stdio ( false ); 
   cin   >>   n   >>   q ; 
   for   ( int   i   =   1 ;   i   <=   n ;   ++ i )   cin   >>   a [ i ]; 
   for   ( int   i   =   1 ;   i   <=   n ;   ++ i )   cin   >>   b [ i ]; 
   build (); 
   while   ( q -- )   { 
     cin   >>   op   >>   l   >>   r ; 
     int   x ; 
     switch   ( op )   { 
       case   1 : 
         cin   >>   x ; 
         modify_mn ( l ,   r ,   x ,   0 ); 
         break ; 
       case   2 : 
         cin   >>   x ; 
         modify_mn ( l ,   r ,   x ,   1 ); 
         break ; 
       case   3 : 
         cin   >>   x ; 
         modify_add ( l ,   r ,   x ,   0 ); 
         break ; 
       case   4 : 
         cin   >>   x ; 
         modify_add ( l ,   r ,   x ,   1 ); 
         break ; 
       case   5 : 
         cout   <<   query ( l ,   r ). max_val ()   <<   '\n' ; 
         break ; 
       default : 
         break ; 
     } 
   } 
} 
小结 在第本章节中我们给出了四道例题,分别讲解了基本区间最值操作的维护、多个标记的优先级处理、数集分类的思想以及多个分类的维护。本质上处理区间最值的基本思想就是数集信息的分类维护与高效合并。在下一章节中,我们将探讨历史区间最值的相关问题。
历史最值问题 历史最值不等于可持久化 注意,本章所讲到的历史最值问题不同于所谓的可持久化数据结构。这类特殊的问题我们将其称为历史最值问题。历史最值的问题可以分为三类。
历史最大值 简单地说,一个位置的历史最大值就是当前位置下曾经出现过的数的最大值。形式化地定义,我们定义一个辅助数组 𝐵 B 𝐴 A 𝐴 A m a x max 
∀ 𝑖 ∈ [ 1 , 𝑛 ] ,   𝐵 𝑖 = m a x ( 𝐵 𝑖 , 𝐴 𝑖 ) ∀ i ∈ [ 1 , n ] ,   B i = max ( B i , A i ) 这时,我们将 𝐵 𝑖 B i 
历史最小值 定义与历史最大值类似,在 𝐴 A m i n min 𝐵 𝑖 B i 
历史版本和 辅助数组 𝐵 B 0 0 𝐴 A 𝐵 B 
∀ 𝑖 ∈ [ 1 , 𝑛 ] ,   𝐵 𝑖 = 𝐵 𝑖 + 𝐴 𝑖 ∀ i ∈ [ 1 , n ] ,   B i = B i + A i 我们称 𝐵 𝑖 B i 𝑖 i 
接下来,我们将历史最值问题分成四类讨论。
可以用标记处理的问题 CPU 监控 序列 𝐴 , 𝐵 A , B 
 对 𝐴 A 𝑥 x  对 𝐴 A 𝑥 x  询问 𝐴 A m a x max  询问 𝐵 B m a x max  每次操作后,我们都进行一次更新,∀ 𝑖   ∈ [ 1 , 𝑛 ] ,   𝐵 𝑖   = m a x ( 𝐵 𝑖 , 𝐴 𝑖 ) ∀ i ∈ [ 1 , n ] ,   B i = max ( B i , A i ) 𝑛 , 𝑚   ≤ 1 0 5 n , m ≤ 10 5 
我们先不考虑操作 1。那么只有区间加的操作,我们维护标记 𝐴 𝑑 𝑑 A d d m a x max m a x max 𝑃 𝑟 𝑒 P r e 𝐴 𝑑 𝑑 A d d 
这个定义可能比较模糊。因此我们先解释一下标记的生存周期。一个标记会经历这样的过程:
在结点 𝑢 u  在结点 𝑢 u  结点 𝑢 u 𝑢 u 𝑢 u  我们认为在这个过程中,从 1 开始到 3 之前,都是结点 𝑢 u 
为什么要定义生存周期?利用这个概念,我们可以证明:在一个结点标记的生存周期内,其子结点均不会发生任何变化,并保留在这个生存周期之前的状态。道理很简单,因为在这个期间你是没有下传标记的。
于是,你就可以保证,在当前标记生存周期内的历史 𝐴 𝑑 𝑑 A d d 𝑢 u 𝑠 s 
𝑃 𝑟 𝑒 𝑠 = m a x ( 𝑃 𝑟 𝑒 𝑠 , 𝑃 𝑟 𝑒 𝑢 + 𝐴 𝑑 𝑑 𝑠 ) , 𝐴 𝑑 𝑑 𝑠 = 𝐴 𝑑 𝑑 𝑢 + 𝐴 𝑑 𝑑 𝑠 P r e s = max ( P r e s , P r e u + A d d s ) , A d d s = A d d u + A d d s 那么信息的更新也是类似的,拿对应的标记更新即可。
接下来,我们考虑操作 1。
区间覆盖操作,会把所有的数变成一个数。在这之后,无论是区间加减还是覆盖,整个区间的数仍是同一个(除非你结束当前标记的生存周期,下传标记)。因此我们可以把第一次区间覆盖后的所有标记都看成区间覆盖标记。也就是说一个标记的生存周期被大致分成两个阶段:
若干个加减操作标记的合并,没有接收过覆盖标记。 覆盖操作的标记,没有所谓的加减标记(加减标记转化为覆盖标记) 于是我们把这个结点的 Pre 标记拆成 ( 𝑃 1 , 𝑃 2 ) ( P 1 , P 2 ) 𝑃 1 P 1 𝑃 2 P 2 𝑂 ( 𝑚 l o g  𝑛 ) O ( m log  n ) 𝑥 x 
  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 
 92 
 93 
 94 
 95 
 96 
 97 
 98 
 99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 #include   <algorithm> 
#include   <climits> 
#include   <iostream> 
using   namespace   std ; 
using   ll   =   long   long ; 
constexpr   int   N   =   1e5   +   7 ; 
struct   Tree   { 
   int   mx ,   _mx ;    // 区间最大值 区间历史最大值 
   int   ad ,   _ad ;    // 区间加标记 区间阶段历史最大加标记 
   int   st ,   _st ;    // 区间修改值 区间阶段历史最大修改标记 
}   g [ N   *   4 ]; 
int   a [ N ]; 
int   n ,   m ; 
#define ls u * 2 
#define rs u * 2 + 1 
#define mid (l + r) / 2 
void   pushup ( int   u )   { 
   g [ u ]. mx   =   max ( g [ ls ]. mx ,   g [ rs ]. mx ); 
   g [ u ]. _mx   =   max ( g [ ls ]. _mx ,   g [ rs ]. _mx ); 
} 
void   pushadd ( int   u ,   int   v ,   int   _v )   { 
   g [ u ]. _mx   =   max ( g [ u ]. _mx ,   g [ u ]. mx   +   _v ),   g [ u ]. mx   +=   v ; 
   if   ( g [ u ]. st   ==   INT_MIN ) 
     g [ u ]. _ad   =   max ( g [ u ]. _ad ,   g [ u ]. ad   +   _v ),   g [ u ]. ad   +=   v ; 
   else 
     g [ u ]. _st   =   max ( g [ u ]. _st ,   g [ u ]. st   +   _v ),   g [ u ]. st   +=   v ; 
} 
void   pushset ( int   u ,   int   v ,   int   _v )   { 
   g [ u ]. _mx   =   max ( g [ u ]. _mx ,   _v ),   g [ u ]. mx   =   v ; 
   g [ u ]. _st   =   max ( g [ u ]. _st ,   _v ),   g [ u ]. st   =   v ; 
} 
void   pushdown ( int   u ,   int   l ,   int   r )   { 
   if   ( g [ u ]. ad   ||   g [ u ]. _ad ) 
     pushadd ( ls ,   g [ u ]. ad ,   g [ u ]. _ad ),   pushadd ( rs ,   g [ u ]. ad ,   g [ u ]. _ad ), 
         g [ u ]. ad   =   g [ u ]. _ad   =   0 ; 
   if   ( g [ u ]. st   !=   INT_MIN   ||   g [ u ]. _st   !=   INT_MIN ) 
     pushset ( ls ,   g [ u ]. st ,   g [ u ]. _st ),   pushset ( rs ,   g [ u ]. st ,   g [ u ]. _st ), 
         g [ u ]. st   =   g [ u ]. _st   =   INT_MIN ; 
} 
void   build ( int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   g [ u ]. _st   =   g [ u ]. st   =   INT_MIN ; 
   if   ( l   ==   r )   { 
     g [ u ]. mx   =   a [ l ]; 
     g [ u ]. _mx   =   a [ l ]; 
     return ; 
   } 
   build ( ls ,   l ,   mid ),   build ( rs ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
int   L ,   R ; 
void   add ( int   v ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L )   return ; 
   if   ( L   <=   l   &&   r   <=   R )   return   pushadd ( u ,   v ,   max ( v ,   0 )); 
   pushdown ( u ,   l ,   r ); 
   add ( v ,   ls ,   l ,   mid ),   add ( v ,   rs ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
void   tset ( int   v ,   int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L )   return ; 
   if   ( L   <=   l   &&   r   <=   R )   return   pushset ( u ,   v ,   v ); 
   pushdown ( u ,   l ,   r ); 
   tset ( v ,   ls ,   l ,   mid ),   tset ( v ,   rs ,   mid   +   1 ,   r ); 
   pushup ( u ); 
} 
int   qmax ( int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L )   return   INT_MIN ; 
   if   ( L   <=   l   &&   r   <=   R )   return   g [ u ]. mx ; 
   pushdown ( u ,   l ,   r ); 
   return   max ( qmax ( ls ,   l ,   mid ),   qmax ( rs ,   mid   +   1 ,   r )); 
} 
int   qmaxh ( int   u   =   1 ,   int   l   =   1 ,   int   r   =   n )   { 
   if   ( R   <   l   ||   r   <   L )   return   INT_MIN ; 
   if   ( L   <=   l   &&   r   <=   R )   return   g [ u ]. _mx ; 
   pushdown ( u ,   l ,   r ); 
   return   max ( qmaxh ( ls ,   l ,   mid ),   qmaxh ( rs ,   mid   +   1 ,   r )); 
} 
int   main ()   { 
   cin . tie ( nullptr ) -> sync_with_stdio ( false ); 
   cin   >>   n ; 
   for   ( int   i   =   1 ;   i   <=   n ;   ++ i )   cin   >>   a [ i ]; 
   build (); 
   int   m ,   z ; 
   cin   >>   m ; 
   for   ( int   i   =   1 ;   i   <=   m ;   ++ i )   { 
     char   op ; 
     cin   >>   op ; 
     while   ( op   ==   ' '   ||   op   ==   '\r'   ||   op   ==   '\n' )   cin   >>   op ; 
     cin   >>   L   >>   R ; 
     int   x ; 
     if   ( op   ==   'Q' ) 
       cout   <<   qmax ()   <<   '\n' ; 
     else   if   ( op   ==   'A' ) 
       cout   <<   qmaxh ()   <<   '\n' ; 
     else   if   ( op   ==   'P' ) 
       cin   >>   x ,   add ( x ); 
     else 
       cin   >>   x ,   tset ( x ); 
   } 
   return   0 ; 
} 
2025/4/6 19:36:35 ,更新历史 在 GitHub 上编辑此页! StudyingFather , H-J-Granger , sshwy , countercurrent-time , Enter-tainer , NachtgeistW , CCXXXI , Ir1d , AngelKitty , cjsoft , diauweb , Early0v0 , ezoixx130 , GekkaSaori , Konano , lanruixiang , LovelyBuggies , Makkiy , mgt , minghu6 , opsiff , P-Y-Y , PotassiumWings , SamZhangQingChuan , Suyun514 , weiyong1024 , GavinZhengOI , Gesrua , GoatGirl98 , hsfzLZH1 , ImpleLee , kenlig , ksyx , kxccc , lychees , ouuan , Peanut-Tang , SukkaW , Tiphereth-A , Xeonacid CC BY-SA 4.0  和 SATA