线段树

最近做了好几题线段树的题目,

对线段树也有了更深的了解.

线段树概念:

线段树是建立在线段的基础上,每个结点都代表了一条线段[a , b]。长度为1的线段称为元线段。非元线段都有两个子结点,左结点代表的线段为[a , (a + b ) / 2],右结点代表的线段为[( a + b ) / 2 , b]。

 

线段树结构如下:

struct Tree { int left, right; other....//其他节点信息 };

包含左右边界(范围)值,

其他的节点信息则视具体题目而定,

这也是线段树精华之所在,

根据不同的题目, 在节点中增加不同的信息,

然后动态的维护/查询.

 

一般线段树包含下面3个操作:

(1).built: 建树

(2).update: 更新

(3).find: 查询

(4).insert: 插入

(5).delete: 删除

 由线段树的定义可知, 线段树其实就是一颗完全二叉树,

所以建树时间复杂度为O(n), 其他操作的时间复杂度均O(Log n).

 

练习:

poj 2777 Count Color

http://poj.org/problem?id=2777

注意更新操作.

#include using namespace std; #define MAXN 100001 struct Tree { int left, right; int color; bool flag; }tree[MAXN*4]; int tot; int getNum(int color) { int ans = 0; while(color) { if(color % 2 != 0) ans += 1; color>>=1; } return ans; } void bulid(int L, int R, int I) { tree[I].left = L; tree[I].right = R; tree[I].color = 1; tree[I].flag = 0; if(L != R) { int mid = (L + R)>>1; bulid(L, mid, I<<1); bulid(mid+1, R, (I<<1)+1); } } void update(int L, int R, int I, int color) { if(L >= tree[I].left && R <= tree[I].right && tree[I].flag) { if(tree[I].left != tree[I].right) { tree[I<<1].color = tree[I].color; tree[I<<1].flag = 1; tree[(I<<1)+1].color = tree[I].color; tree[(I<<1)+1].flag = 1; tree[I].flag = 0; } //return; } if(L == tree[I].left && R == tree[I].right) { tree[I].color = color; tree[I].flag = 1; return; } int mid = (tree[I].left + tree[I].right)>>1; if(R <= mid) { update(L, R, I<<1, color); } else if(L > mid) { update(L, R, (I<<1)+1, color); } else { update(L, mid, I<<1, color); update(mid+1, R, (I<<1)+1, color); } tree[I].color = tree[I<<1].color | tree[(I<<1)+1].color; } void getColor(int L, int R, int I) { if(L >= tree[I].left && R <= tree[I].right && getNum(tree[I].color) == 1) { tot |= tree[I].color; return; } if(L == tree[I].left && R == tree[I].right) { tot |= tree[I].color; return; } int mid = (tree[I].left + tree[I].right)>>1; if(R <= mid) { getColor(L, R, I<<1); } else if(L > mid) { getColor(L, R, (I<<1)+1); } else { getColor(L, mid, I<<1); getColor(mid+1, R, (I<<1)+1); } } int main() { int N, T, O; char op; int a, b, c; while(scanf("%d%d%d", &N, &T, &O) != EOF) { bulid(1, N, 1); while(O--) { scanf("%*c%c", &op); if(op == 'C') { scanf("%d%d%d", &a, &b, &c); if(a > b) swap(a, b); update(a, b, 1, 1<<(c-1)); } else if(op == 'P') { scanf("%d%d", &a, &b); if(a > b) swap(a, b); tot = 0; getColor(a, b, 1); printf("%d/n", getNum(tot)); } } } return 0; }

 

poj 3468  A Simple Problem with Integers

http://poj.org/problem?id=3468

注意下数值的取值范围就好了.

#include #include #include #define NMAX 100001 struct Tree { int left, right; __int64 sum, add; }tree[NMAX * 4]; __int64 v[NMAX], sum; void bulid(int L, int R, int I) { tree[I].left = L; tree[I].right = R; tree[I].add = 0; if(R != L) { int mid = (R + L)>>1; bulid(L, mid, I<<1); bulid(mid+1, R, (I<<1)+1); } else { tree[I].sum = v[R]; return; } tree[I].sum = tree[I<<1].sum + tree[(I<<1)+1].sum; } void update(int L, int R, int I, __int64 num) { if(tree[I].left >= L && tree[I].right <= R) { tree[I].add += num; return; } else if(tree[I].left <= L && tree[I].right >= R) { tree[I].sum += ((R - L + 1) * num); } else if(tree[I].left >= L && tree[I].right >= R) { tree[I].sum += ((R - tree[I].left + 1) * num); } else if(tree[I].left <= L && tree[I].right <= R) { tree[I].sum += ((tree[I].right - L + 1) * num); } int mid = (tree[I].left + tree[I].right)>>1; if(mid >= L) update(L, R, I<<1, num); if(mid + 1 <= R) update(L, R, (I<<1)+1, num); } void getSum(int L, int R, int I) { if(tree[I].left >= L && tree[I].right <= R) { sum += (tree[I].sum + (tree[I].add * (tree[I].right - tree[I].left + 1))); return ; } else { tree[I<<1].add += tree[I].add; tree[(I<<1)+1].add += tree[I].add; tree[I].sum += (tree[I].add * (tree[I].right - tree[I].left + 1)); tree[I].add = 0; } int mid = (tree[I].left + tree[I].right)>>1; if(mid >= L) getSum(L, R, I<<1); if(mid+1 <= R) getSum(L, R, (I<<1)+1); } int main() { //freopen("in.txt", "r", stdin); int N, Q; int a, b, c; char op; while(scanf("%d%d", &N, &Q) != EOF) { for(int i=1; i<=N; i++) scanf("%I64d", &v[i]); bulid(1, N, 1); while(Q--) { scanf("%*c%c", &op); if(op == 'Q') { scanf("%d%d", &a, &b); sum = 0; getSum(a, b, 1); printf("%I64d/n", sum); } else { scanf("%d%d%d", &a, &b, &c); update(a, b, 1, c); } } } return 0; }

 

poj 3277 City Horizon

http://poj.org/problem?id=3277

(1 ≤ Ai < Bi ≤ 1,000,000,000)所以需要离散化,

离散化的方法是: 把所有A,B的值先剔重然后排序, 将排序后的1-N个数映射到线段树中.

ps: 貌似这题数组要开4倍以上, 我开4倍RE了.....

#include #include #include using namespace std; #define NMAX 40010 struct Tree { int L, R, H; Tree() { this->H = 0; } }tree[NMAX*8]; int N; int L[NMAX], R[NMAX], H[NMAX], tmp[NMAX*2]; void bulid(int I, int L, int R) { tree[I].L = L; tree[I].R = R; if(R - L > 1) { int mid = (L + R)>>1; bulid(2 * I, L, mid); bulid(2 * I + 1, mid, R); } } void insert(int I, int L, int R, int H) { if(L == tmp[tree[I].L] && R == tmp[tree[I].R]) { //正好 if(H > tree[I].H) tree[I].H = H; return; } int mid = (tree[I].L + tree[I].R)>>1; int val = tmp[mid]; if(R <= val) { //在左边 insert(2 * I, L, R, H); } else if(L >= val) { //在右边 insert(2 * I + 1, L, R, H); } else { //分布在两边 insert(2 * I, L, val, H); insert(2 * I + 1, val, R, H); } } __int64 ans(int I, int H) { if(H > tree[I].H) { tree[I].H = H; } if(tree[I].R - tree[I].L <= 1) { return (__int64)(tmp[tree[I].R] - tmp[tree[I].L]) * tree[I].H; } return ans(I * 2, tree[I].H) + ans(I * 2 + 1, tree[I].H); } int main() { //freopen("in.txt", "r", stdin); scanf("%d", &N); int i, j, r; for(i=1,j=1; i<=N; i++) { scanf("%d%d%d", &L[i], &R[i], &H[i]); tmp[j++] = L[i]; tmp[j++] = R[i]; } sort(tmp + 1, tmp + j); r = 2; for(i=2; i

 

poj 2832 Sliding Window

http://poj.org/problem?id=2823

ps: 跑了7469MS, 囧

#include using namespace std; #define MAXN 1000001 #define max(a,b) a>b?a:b #define min(a,b) a>b?b:a struct Tree { int left, right; int root; int min, max; }tree[MAXN*4]; int data[MAXN], minl[MAXN], maxl[MAXN]; void build(int L, int R, int I) { tree[I].left = L; tree[I].right = R; if(L == R) { tree[I].min = tree[I].max = data[L]; return; } else { int mid=(L + R)>>1; build(L, mid, I<<1); build(mid+1, R, (I<<1)+1); tree[I].min = min(tree[I<<1].min, tree[(I<<1)+1].min); tree[I].max = max(tree[I<<1].max, tree[(I<<1)+1].max); } } void query(int L, int R, int& min, int& max, int I) { if(tree[I].left == L && tree[I].right == R) { min = tree[I].min; max = tree[I].max; return; } int mid = (tree[I].left + tree[I].right)>>1; if(mid >= R) { query(L, R, min, max, I<<1); } else if(mid < L) { query(L, R, min, max, (I<<1)+1); } else { int t1, t2; query(L, mid, min, max, I<<1); query(mid+1, R, t2, t1, (I<<1)+1); min = min < t2 ? min : t2; max = max > t1 ? max : t1; } } int main() { int n, len; int maxt, mint; int i; while(scanf("%d%d", &n, &len) != EOF) { for(i=1; i<=n; i++) scanf("%d", &data[i]); build(1, n, 1); for(i=1; i<=n-len+1; i++) { query(i, i+len-1, mint, maxt, 1); minl[i] = mint; maxl[i] = maxt; } for(i=1; i<=n-len+1; i++) printf("%d ", minl[i]); puts(""); for(i=1; i<=n-len+1; i++) printf("%d ", maxl[i]); puts(""); } return 0; }

其他的题目:poj2828 poj2812.....

 

你可能感兴趣的:(ACM专题)