线段树小结
1 线段树小结篇: 2 基本是按着HH专辑来练的:http://www.notonlysuccess.com/index.php/segment-tree-complete/ 3 按分类为单点更新,成段更新,区间合并,扫描线; 4 做了一些题目,显然对于明显的操作如单纯的单点更新,敲出代码是不成问题的; 5 但对于稍微麻烦一点的操作,如果没有很好的代码能力和平时的积累,就很难在比赛时一气呵成写出代码; 6 因此写本文在于总结线段树代码的一些细节之处,给自己以后遗忘复习用; 7 8 1:lazy标记; 9 lazy标记主要运用于成段更新时,这也是线段树经常用到的,因为每次更新只需要更新到当前位置,
等到下次更新或者询问时再更新,使得更新的复杂度降低。 10 lazy标记对于初写线段数的人来说是个不小的挑战,刚开始写成段更新时,可能会遇到的问题: 11 a:lazy标记是对于当前节点rt来说,还是对于子节点rt<<1,rt<<1|1来说,
即对当前col[rt],当pushdown(rt)时,是更新rt节点的值,还是更新子节点的值? 14 b: 在update()时,lazy标记会被更新,此时要不要对当前rt更新值? 15 16 这些问题主要是因为当时不明白lazy标记到底表示的是什么意思; 17 col[rt]的含义: col[rt]标记为空; 18 col[rt]不为空: 当前段被标记,
当update(),query()到当前段是,要先pushdown()下去,再递归到子节点; 19 21 lazy标记的精髓就在于延迟操作,但提前是延迟操作不会使结果不正确,那为什么不会使结果不正确呢? 22 因为线段树操作不管是update(),query()都是从根节点开始的,
当询问或更新到需要的当前段后,已经完成了操作, 23 24 我们直接从该段pushup()上去,不会导致最终解错误,对于该段的子段来说我们不需要它的解,
所以我们把这些我们当前不需要的段不进行更新, 25 26 用lazy标记记录下来, 当需要的时候先把它更新了,再进行必要操作; 27 28 也就是col[rt]不为空时,它只对子节点产生影响,
并且col[rt]不为空时,sum[rt],cnt[rt],rt节点的值都是正确的,在pushudown()时, 29 30 把标记传给col[rt<<1]和col[rt<<1|1],所以rt<<1,和rt<<1|1的值就要更新,
col[rt]已经把标记传下去了,所以要清空;在update()时,被标记的当前段也要更新为正确值;
http://acm.hdu.edu.cn/showproblem.php?pid=3397
View Code
2:离散化的一些细节;
a:对于数据范围超过10^6,和double类型的数据都要进行离散化,但离散化后一般会遇到一些问题,
线段树里的一个节点代表的是一段距离,但离散后一个点就是一个点;比如更新(1,3),会更新到(1,2)(3,3)
如果单纯的计算距离的会3-3=0,so在更新的时候要处理一下更新(1,3),传入的值为(1,2)然后在计算距离是用xi[3]-xi[1]
b:原先代表是一段区域;
c:要考虑端点和中间,要乘2;
1 //hdu3397 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<cmath> 7 #include<algorithm> 8 #define lson l,m,rt<<1 9 #define rson m+1,r,rt<<1|1 10 using namespace std; 11 const int MAXN=111111; 12 int mx_0[MAXN<<2];//改段连续0的个数 13 int mx_1[MAXN<<2];//该段连续1的个数 14 int conl_1[MAXN<<2],conr_1[MAXN<<2];//左边连续1的个数,右边连续1的个数 15 int conl_0[MAXN<<2],conr_0[MAXN<<2];//同上 16 int cnt[MAXN<<2];//记录该段1的个数 17 int col[MAXN<<2];//lazy标记 18 int n,m; 19 void pushup(int l,int m,int r,int rt) 20 { 21 cnt[rt]=cnt[rt<<1]+cnt[rt<<1|1]; 22 if (conl_1[rt<<1]==m-l+1) 23 { 24 conl_1[rt]=conl_1[rt<<1]+conl_1[rt<<1|1]; 25 } else conl_1[rt]=conl_1[rt<<1]; 26 if (conr_1[rt<<1|1]==r-m) 27 { 28 conr_1[rt]=conr_1[rt<<1|1]+conr_1[rt<<1]; 29 } else conr_1[rt]=conr_1[rt<<1|1]; 30 31 if (conl_0[rt<<1]==m-l+1) 32 { 33 conl_0[rt]=conl_0[rt<<1]+conl_0[rt<<1|1]; 34 } else conl_0[rt]=conl_0[rt<<1]; 35 if (conr_0[rt<<1|1]==r-m) 36 { 37 conr_0[rt]=conr_0[rt<<1|1]+conr_0[rt<<1]; 38 } else conr_0[rt]=conr_0[rt<<1|1]; 39 40 int tmx=conr_1[rt<<1]+conl_1[rt<<1|1]; 41 mx_1[rt]=max(tmx,max(mx_1[rt<<1],mx_1[rt<<1|1])); 42 43 tmx=conr_0[rt<<1]+conl_0[rt<<1|1]; 44 mx_0[rt]=max(tmx,max(mx_0[rt<<1],mx_0[rt<<1|1])); 45 } 46 void build(int l,int r,int rt) 47 { 48 col[rt]=-1; 49 if (l==r) 50 { 51 scanf("%d",&col[rt]); 52 if (col[rt]==1){ 53 mx_0[rt]=0; 54 conl_0[rt]=conr_0[rt]=0; 55 mx_1[rt]=conl_1[rt]=conr_1[rt]=cnt[rt]=1; 56 } 57 else { 58 mx_0[rt]=1; 59 cnt[rt]=mx_1[rt]=0; 60 conl_0[rt]=conr_0[rt]=1; 61 conl_1[rt]=conr_1[rt]=0; 62 } 63 return; 64 } 65 int m=(l+r)>>1; 66 build(lson); 67 build(rson); 68 pushup(l,m,r,rt); 69 } 70 void pushdown(int l,int m,int r,int rt)//还是lazy标志的改变是难点; 71 { 72 if (col[rt]!=-1) 73 { 74 if (col[rt]==1) 75 { 76 col[rt<<1]=1; 77 conl_1[rt<<1]=conr_1[rt<<1]=mx_1[rt<<1]=m-l+1; 78 conl_0[rt<<1]=conr_0[rt<<1]=mx_0[rt<<1]=0; 79 cnt[rt<<1]=m-l+1; 80 81 col[rt<<1|1]=1; 82 conl_1[rt<<1|1]=conr_1[rt<<1|1]=mx_1[rt<<1|1]=r-m; 83 conl_0[rt<<1|1]=conr_0[rt<<1|1]=mx_0[rt<<1|1]=0; 84 cnt[rt<<1|1]=r-m; 85 }else if (col[rt]==0) 86 { 87 col[rt<<1]=0; 88 conl_1[rt<<1]=conr_1[rt<<1]=mx_1[rt<<1]=0; 89 conl_0[rt<<1]=conr_0[rt<<1]=mx_0[rt<<1]=m-l+1; 90 cnt[rt<<1]=0; 91 92 col[rt<<1|1]=0; 93 conl_1[rt<<1|1]=conr_1[rt<<1|1]=mx_1[rt<<1|1]=0; 94 conl_0[rt<<1|1]=conr_0[rt<<1|1]=mx_0[rt<<1|1]=r-m; 95 cnt[rt<<1|1]=0; 96 }else if (col[rt]==2) 97 { 98 99 swap(conl_0[rt<<1],conl_1[rt<<1]); 100 swap(conr_0[rt<<1],conr_1[rt<<1]); 101 swap(mx_0[rt<<1],mx_1[rt<<1]); 102 cnt[rt<<1]=m-l+1-cnt[rt<<1]; 103 104 swap(conl_0[rt<<1|1],conl_1[rt<<1|1]); 105 swap(conr_0[rt<<1|1],conr_1[rt<<1|1]); 106 swap(mx_0[rt<<1|1],mx_1[rt<<1|1]); 107 cnt[rt<<1|1]=r-m-cnt[rt<<1|1]; 108 109 if (col[rt<<1]==0)//子段的标记不同,影响也不同 110 { 111 col[rt<<1]=1; 112 } else if (col[rt<<1]==1) 113 { 114 col[rt<<1]=0; 115 } else if (col[rt<<1]==2) 116 { 117 col[rt<<1]=-1; 118 } else if (col[rt<<1]==-1) 119 { 120 col[rt<<1]=2; 121 } 122 123 if (col[rt<<1|1]==0) 124 { 125 col[rt<<1|1]=1; 126 } else if (col[rt<<1|1]==1) 127 { 128 col[rt<<1|1]=0; 129 } else if (col[rt<<1|1]==2) 130 { 131 col[rt<<1|1]=-1; 132 } else if (col[rt<<1|1]==-1) 133 { 134 col[rt<<1|1]=2; 135 } 136 137 } 138 col[rt]=-1; //清除lazy标志 139 } 140 } 141 void update(int L,int R,int c,int l,int r,int rt) 142 { 143 if (L<=l && r<=R) 144 { 145 if (c==2) 146 { 147 swap(conl_0[rt],conl_1[rt]); 148 swap(conr_0[rt],conr_1[rt]); 149 swap(mx_0[rt],mx_1[rt]); 150 cnt[rt]=r-l+1-cnt[rt]; 151 //这里lazy标志的改变,错了好久,只能说lazy标识的不熟悉; 152 //lazy标记只对子段起作用,所以要更新当前段; 153 if (col[rt]==0) 154 { 155 col[rt]=1;//原先标记不同,标记更新也不同; 156 } else if (col[rt]==1) 157 { 158 col[rt]=0; 159 } else if (col[rt]==2) 160 { 161 col[rt]=-1; 162 } else if (col[rt]==-1) 163 { 164 col[rt]=2; 165 } 166 }else if (c==1) 167 { 168 conl_1[rt]=conr_1[rt]=mx_1[rt]=r-l+1; 169 conl_0[rt]=conr_0[rt]=mx_0[rt]=0; 170 cnt[rt]=r-l+1; 171 172 col[rt]=1;//更新标记; 173 }if (c==0) 174 { 175 conl_1[rt]=conr_1[rt]=mx_1[rt]=0; 176 conl_0[rt]=conr_0[rt]=mx_0[rt]=r-l+1; 177 cnt[rt]=0; 178 179 col[rt]=0; 180 } 181 182 return; 183 } 184 int m=(l+r)>>1; 185 pushdown(l,m,r,rt); 186 if (L<=m) update(L,R,c,lson); 187 if (m< R) update(L,R,c,rson); 188 pushup(l,m,r,rt); 189 } 190 int query_num(int L,int R,int l,int r,int rt)//统计个数 191 { 192 if (L<=l && r<=R) 193 { 194 return cnt[rt]; 195 } 196 int m=(l+r)>>1; 197 pushdown(l,m,r,rt);//每次询问都要先pushdown(); 198 int t1,t2; 199 t1=t2=0; 200 if (L<=m) t1=query_num(L,R,lson); 201 if (m< R) t2=query_num(L,R,rson); 202 return t1+t2; 203 } 204 int query_con(int L,int R,int l,int r,int rt)//统计连续1 205 { 206 if (L<=l && r<=R) 207 { 208 return mx_1[rt]; 209 } 210 int m=(l+r)>>1; 211 pushdown(l,m,r,rt); 212 int t1,t2; 213 t1=t2=0; 214 if (L<=m) t1=query_con(L,R,lson); 215 if (m< R) t2=query_con(L,R,rson); 216 int cl,cr,tmx=0; 217 if (L<=m && m<R) 218 { 219 cl=min(conr_1[rt<<1],m-L+1); 220 cr=min(conl_1[rt<<1|1],R-m); 221 tmx=cl+cr; 222 } 223 return max(tmx,max(t1,t2)); 224 } 225 int main() 226 { 227 int T; 228 scanf("%d",&T); 229 while (T--) 230 { 231 scanf("%d%d",&n,&m); 232 build(1,n,1); 233 while (m--) 234 { 235 int a,b,c; 236 scanf("%d%d%d",&a,&b,&c); 237 b++;c++; 238 if (a==0) 239 { 240 update(b,c,a,1,n,1); 241 }else if (a==1) 242 { 243 update(b,c,a,1,n,1); 244 245 }else if (a==2) 246 { 247 update(b,c,a,1,n,1); 248 }else if (a==3) 249 { 250 int t=query_num(b,c,1,n,1); 251 printf("%d\n",t); 252 }else if (a==4) 253 { 254 int t=query_con(b,c,1,n,1); 255 printf("%d\n",t); 256 } 257 258 } 259 } 260 return 0; 261 }