线段树(二):成段更新
POJ 2777
/* 线段区间染色,并统计区间内颜色个数 因为颜色不多可以用位运算,但是我还是用数组记录 注意:C的>>和<<运算符比+ - 低,x << 1 + 1来表示x * 2 + 1是错的,必须套上括号 即(x << 1) + 1,已经不止一次犯这个错误了。 精简了一下代码。但还是上百行…… 2011-07-29 07:59 */ #include <stdio.h> #define MAXN 1000000 struct node { int l, r, c; }t[MAXN]; int c[32]; void build(int l, int r, int x) { t[x].l = l; t[x].r = r; t[x].c = 1; if (l == r) return; int m = (l + r) >> 1; build(l, m, x << 1); build(m + 1, r, (x << 1) + 1); } void update(int l, int r, int c, int x) { if ((l == t[x].l) && (r == t[x].r)) return t[x].c = c; // else if (t[x].l < t[x].r) { int m = (t[x].l + t[x].r) >> 1; if (t[x].c >= 1) { t[x << 1].c = t[(x << 1) + 1].c = t[x].c; t[x].c = -1; } if (r <= m) update(l, r, c, x << 1); else if (l > m) update(l, r, c, (x << 1) + 1); else { update(l, m, c, (x << 1)); update(m + 1, r, c, (x << 1) + 1); } } } void query(int l, int r, int x) { if (t[x].c > 0) return c[t[x].c] = 1; // else if (t[x].c == -1 && t[x].l < t[x].r) { int m = (t[x].l + t[x].r) >> 1; if (r <= m) query(l, r, x << 1); else if (l > m) query(l, r, (x << 1) + 1); else { query(l, m, x << 1); query(m + 1, r, (x << 1) + 1); } } } int count() { int i, sum = 0; for(i = 1; i <= 30; i++) sum += c[i]; return sum; } void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int main() { int n, t, o; while (scanf("%d%d%d", &n, &t, &o) == 3) { build(1, n, 1); while (o--) { char ch[2]; scanf("%s", &ch); if (ch[0] == 'C') { int a, b, c; scanf("%d%d%d", &a, &b, &c); if (a > b) swap(&a, &b); update(a, b, c, 1); } else { int a, b; scanf("%d%d", &a, &b); if (a > b) swap(&a, &b); memset(c, 0, sizeof(c)); query(a, b, 1); printf("%d\n", count()); } } } return 0; }
HDU 1698
/* 题意:初始时n个权值为1的技能,给出m条改变区间[x..y]的技能为1,2或3的指令,最后求技能总的权值 算法:成段更新,区间求和,线段树维护。 体会:有时候C语言也要用G++交才能过。Debug好久,主要是因为改变技能时按照原来写的线段树的套路写的,对于不同的要求要灵活运用,不能生搬硬套 */ #include <stdio.h> #define MAXN 400000 #define ROOT 1 struct { int left,right,cover; }t[MAXN]; //cover域表示所控制区间内的技能,若为-1则为多种 void build(int p, int left, int right) { t[p].left = left; t[p].right = right; t[p].cover = 1; if (left == right) return; int m = (left + right) / 2; build(p*2, left, m); build(p*2+1, m+1, right); }//建树,初始时都是为1的技能 void change(int p, int left , int right, int color) { if ((t[p].left >= left) && (t[p].right <= right)) { t[p].cover = color; return; }//如果树控制的区间都要染色那么改一下cover域就行了 if (t[p].cover != -1) { t[p*2].cover = t[p*2+1].cover = t[p].cover; t[p].cover = -1; }//否则如果树控制区间的颜色是同一种,则可能变成多种,cover改为-1,而且在上一种情况中左右子树没有更新,现在要用了就要更新一下 int m = (t[p].left + t[p].right) / 2; if (left <= m) change(p*2, left, right, color); if (right > m ) change(p*2+1, left, right, color); //错就错在这步,只要涉及左子树就更新左子树,涉及右子树就更新右子树,原来写成如果在左子树范围内更新左子树,在右子树范围内就更新右子树了。模仿在网上看的程序,以为可以少些点的,但是写错了,偷懒失败。 } int count(int p, int left, int right) { int m = (t[p].left + t[p].right) / 2; if (t[p].left == left && t[p].right == right) { if (t[p].cover != -1) return t[p].cover * (t[p].right - t[p].left + 1); else return count(p*2, left, m) + count(p*2+1, m+1, right); }//统计,如果在该树单调则统计否则分别统计左右子树 if (m >= right) return count(p*2, left, m); else if (m < left) return count(p*2+1, m+1, right); else return count(p*2, left, m) + count(p*2+1, m+1, right); //不包括整棵树的话就分别统计了 } //其实我觉得这道题是统计整棵树,那么count过程似乎可以更简单,但没仔细想 int main() { int i,j,k,m,n,t,x,y,z; scanf("%d", &t); for (k = 1; k <= t; k++) { scanf("%d%d", &n, &m); build(ROOT, 1, n); for (i = 1; i <= m; i++) { scanf("%d%d%d", &x, &y, &z); change(ROOT, x, y, z); } printf("Case %d: The total value of the hook is %d.\n",k,count(1,1,n)); }//注意输出有个. return 0; }