线段树总结
模板:
点修改:
1 int sum[maxn<<2], maxv[maxn<<2], minv[maxn<<2]; 2 3 void maintain(int u) 4 { 5 int lc = u*2, rc = u*2+1; 6 sum[u] = sum[lc] + sum[rc]; 7 maxv[u] = max(maxv[lc], maxv[rc]); 8 minv[u] = min(minv[lc]. minv[rc]); 9 } 10 11 void build(int u, int l, int r) 12 { 13 if(l==r) 14 { 15 sum[u] = maxv[u] = minv[u] = 0; 16 return; 17 } 18 19 int mid = (l+r)>>1; 20 build(u*2, l, mid); 21 build(u*2+1, mid+1, r); 22 maintain(u); 23 } 24 25 void add_val(int u,int l,int r,int x, int val) 26 { 27 if(l==r) 28 { 29 sum[u] += val; 30 maxv[u] += val; 31 minv[u] += val; 32 return ; 33 } 34 35 int mid = (l+r)>>1; 36 if(x<=mid) add_val(u*2, l, mid, x, val); 37 else add_val(u*2+1, mid+1, r, x, val); 38 maintain(u); 39 } 40 41 void set_val(int u, int l, int r, int x, int val) 42 { 43 if(l==r) 44 { 45 sum[u] = val; 46 maxv[u] = val; 47 minv[u] = val; 48 return; 49 } 50 51 int mid = (l+r)>>1; 52 if(x<=mid) set_val(u*2, l, mid, x, val); 53 else set_val(u*2+1, mid+1, r, x, val); 54 maintain(u); 55 } 56 57 int maxx, minn; 58 int query(int u,int l, int r, int x, int y) 59 { 60 if(x<=l && r<=y) 61 { 62 maxx = max(maxx, maxv[u]); 63 minn = min(minn, minv[u]); 64 return sum[u]; 65 } 66 67 int ret = 0; 68 int mid = (l+r)>>1; 69 if(x<=mid) ret += query(u*2, l, mid, x, y); 70 if(y>=mid+1) ret += query(u*2+1, mid+1, r, x ,y); 71 return ret; 72 }
区间修改:
1 int sum[maxn<<2], maxv[maxn<<2], minv[maxn<<2], addv[maxn<<2], setv[maxn<<2]; 2 3 void pushup(int u) 4 { 5 int l = 2*u, r = 2*u+1; 6 sum[u] = sum[l] + sum[r]; 7 maxv[u] = max(maxv[l], maxv[r]); 8 minv[u] = min(minv[l], minv[r]); 9 } 10 11 void pushdown(int u, int len) 12 { 13 int lc = 2*u, rc = 2*u+1; 14 if(setv[u]>=0) 15 { 16 addv[lc] = addv[rc] = 0; 17 minv[lc] = minv[rc] = maxv[lc] = maxv[rc] = setv[u]; 18 sum[lc] = (len+1)/2*setv[u]; 19 sum[rc] = len/2*setv[u]; 20 setv[lc] = setv[rc] = setv[u]; 21 setv[u] = -1; 22 } 23 24 if(addv[u]) 25 { 26 sum[lc] += (len+1)/2*addv[u]; 27 sum[rc] += len/2*addv[u]; 28 maxv[lc] += addv[u]; minv[lc] += addv[u]; 29 maxv[rc] += addv[u]; minv[rc] += addv[u]; 30 addv[lc] += addv[u]; addv[rc] += addv[u]; 31 addv[u] = 0; 32 } 33 } 34 35 void build(int u, int l, int r) 36 { 37 if(l==r) 38 { 39 sum[u] = maxv[u] = minv[u] = 0; 40 return; 41 } 42 43 int mid = (l+r)>>1; 44 build(u*2, l, mid); 45 build(u*2+1, mid+1, r); 46 pushup(u); 47 } 48 49 void set_val(int u, int l, int r, int x, int y, int val) 50 { 51 if(x<=l && r<=y) 52 { 53 addv[u] = 0; 54 maxv[u] = minv[u] = val; 55 sum[u] = (r-l+1)*val; 56 setv[u] = val; 57 return; 58 } 59 60 pushdown(u, r-l+1); 61 int mid = (l+r)>>1; 62 if(x<=mid) set_val(u*2, l, mid, x, y, val); 63 if(y>=mid+1) set_val(u*2+1, mid+1, r, x, y, val); 64 pushup(u); 65 } 66 67 68 void add_val(int u, int l, int r, int x, int y, int val) 69 { 70 if(x<=l && r<=y) 71 { 72 maxv[u] += val; 73 minv[u] += val; 74 sum[u] += (r-l+1)*val; 75 addv[u] += val; 76 return; 77 } 78 79 pushdown(u, r-l+1); 80 int mid = (l+r)>>1; 81 if(x<=mid) add_val(u*2, l, mid, x, y, val); 82 if(y>=mid+1) add_val(u*2+1, mid+1, r, x, y, val); 83 pushup(u); 84 } 85 86 int ansmax, ansmin; 87 int query(int u, int l, int r, int x, int y) 88 { 89 if(x<=l && r<=y) 90 { 91 ansmax = max(ansmax, maxv[u]); 92 ansmin = min(ansmin, minv[u]); 93 return sum[u]; 94 } 95 96 pushdown(u, r-l+1); 97 int mid = (l+r)>>1; 98 int ret = 0; 99 if(x<=mid) ret += query(u*2, l, mid, x, y); 100 if(y>=mid+1) ret += query(u*2+1, mid+1, r, x, y); 101 return ret; 102 }
一、点修改:
二、区间修改:
POJ3468 A Simple Problem with Integers
HDU4027 Can you answer these queries?(区间开根)
CodeChef - ANDMIN (区间与&)
三、区间染色:
四、区间合并:
SPOJ - GSS1 (区间最大连续和)
五、连续型线段树(求面积、周长、体积):
1.此种线段树的操作对象为连续型,即最小的元素为长度为1的区间[l,r],其中l和r只代表端点(r-l>=1),用于确定区间的位置和长度,在l处和r处没有特别的含义。而以往做的什么单点更新以及普通的区间更新之类的,都属于离散型,他们的最小的操作对象是一个一个的点,因而在l处和r处是有含义的。
2.因为最小的操作对象为一段区间,因而线段树的写法也需要做适当变形:
void push_up(int u, int l, int r) { if(times[u]>0) //该区间被覆盖: 覆盖长度为区间长度 sum[u] = X[r] - X[l]; else //该区间没有被覆盖: 如果为单位区间,则覆盖长度为0,否则为两个子区间的覆盖长度之和。 sum[u] = (l+1==r)?0:sum[u*2]+sum[u*2+1]; } void add(int u, int l, int r, int x, int y, int v) { if(x<=l && r<=y) { times[u] += v; push_up(u, l, r); return; } int mid = (l+r)>>1; if(x<=mid-1) add(u*2, l, mid, x, y, v); //在左端至少要有一个单位区间(为[mid-1, mid],所以x需要满足x<=mid-1) if(y>=mid+1) add(u*2+1, mid, r, x, y, v); //在右端至少要有一个单位区间(为[mid, mid+1],所以y需要满足y>=mid+1) push_up(u, l, r); }
3.为何不需要push_down()和query()函数呢?
答:因为push_down()的本质作用是把修改传递下去,以查询更小的区间的具体信息。但是对于此种类型的线段树,我们只需要获取整段的信息(即sum[1]),而不需要取查询子区间的信息,所有:当修改了某一子区间的后,只需把修改信息往上更新,而不需要理会往下的更小的子区间。因而不需要push_down()和query()函数。
HDU1542 Atlantis (矩形并面积)
HDU1255 覆盖的面积 (矩形交面积)
POJ1177 Picture (矩形并周长)
HDU3642 Get The Treasury (长方体交体积)