线段树(二):成段更新

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;
}


posted on 2011-07-29 08:30  oa414  阅读(423)  评论(0编辑  收藏  举报

导航