题目来源: http://acm.pku.edu.cn/JudgeOnline/problem?id=2777
参考程序:http://www.cnblogs.com/saintqdd/archive/2007/11/04/948622.html
 
第一次学习线段树算法。
题目给出改变颜色和查询操作,对于这个用线段树做比较好。由于颜色比较少,可以用二进制来表示颜色。即每中颜色所占用的数位不一样。这样就很容易的求得每种颜色中不同颜色了。例如1101就是3种颜色。而对2种不同的颜色求或运算,就可以得到他们的颜色之和。1101|1010—>1111就4个颜色了。
线段树就是每个节点表示一段范围,父节点的范围比子节点表示的范围大。对于二叉线段树,可以将父节点的范围2分给子节点。 父节点的颜色就是子节点颜色的或运算。
下面代码,看完也觉得比较易懂的。
 

Code
  1
#include <iostream>
  2
#include <algorithm>
  3
#define MAXM 100001
  4
  5
using namespace std;
  6
  7
struct Seg_Tree //树节点的数据结构
  8

{
  9
    Seg_Tree *leftPtr,*rightPtr; //左右子树的指针
 10
    int left,right;   //节点的表示范围
 11
    int color;     //节点的color
 12
};
 13
 14
int L,T,O,cnt;
 15
int numOfTree;
 16
Seg_Tree tree[MAXM*10],*Root; //所有的节点和根节点
 17
 18
Seg_Tree* creatNode() //创建新节点
 19

{
 20
    Seg_Tree *p= &tree[numOfTree++];
 21
    memset(p,0,sizeof(Seg_Tree));
 22
    return p;
 23
}
 24
 25
Seg_Tree *CreatTree(int s,int e) //创建树
 26

{
 27
    Seg_Tree* root=creatNode(); 
 28
    root->left=s;root->right=e; 
 29
    if(s!=e)
 30
    
{
 31
        int mid=(s+e)/2; //递归分解,节点表示的范围越来越小
 32
        root->leftPtr=CreatTree(s,mid); //子树表示的范围约是父节点的1/2
 33
        root->rightPtr=CreatTree(mid+1,e);
 34
    }
 35
    return root;
 36
}
 37
 38
bool odd(int n) //判断这个颜色是否为单色
 39

{
 40
    return (n&(n-1))==0;
 41
}
 42
 43
void updateTree(Seg_Tree* root,int s,int e,int color) //更新线段树
 44

{
 45
    if(s<=root->left&&e>=root->right) //如果此节点表示的范围完全被包含,则整个被重新改变颜色
 46
    
{
 47
        root->color=color;
 48
        return;
 49
    }
 50
    if(root->color==color) return;  //节点颜色不需要被改变
 51
    if(odd(root->color))  //如果节点为单色,则在上一次改变中,子树没有被改变,这个重新赋值
 52
    
{
 53
        root->leftPtr->color=root->color;
 54
        root->rightPtr->color=root->color;
 55
    }
 56
    int mid=(root->left+root->right)/2;
 57
    if(s<=mid) updateTree(root->leftPtr,s,e,color);  //如果左子树需要被改变
 58
    if(e>mid) updateTree(root->rightPtr,s,e,color);  //右子树被改变
 59
    root->color=root->leftPtr->color|root->rightPtr->color; //父节点的颜色等于2个子树的或和
 60
}
 61
 62
void Query(Seg_Tree* root,int s,int e,int &cnt)  //查询
 63

{
 64
    if(s<=root->left&&e>=root->right) //如果节点被完全包括
 65
    
{
 66
        cnt=cnt|root->color;
 67
        return;
 68
    }
 69
    if(odd(root->color))
 70
    
{
 71
        cnt=cnt|root->color;
 72
        return;
 73
    }
 74
    int mid=(root->left+root->right)/2;
 75
    if(s<=mid) Query(root->leftPtr,s,e,cnt); //查询左子树
 76
    if(e>mid) Query(root->rightPtr,s,e,cnt); //查询右子树
 77
}
 78
 79
int cal(int n) //计算颜色数量
 80

{
 81
    int cnt=0;
 82
    while(n>0)
 83
    
{
 84
        if(n%2) cnt++;
 85
        n>>=1;
 86
    }
 87
    return cnt;
 88
}
 89
 90
int main()
 91

{
 92
    numOfTree=0;
 93
    scanf("%d%d%d",&L,&T,&O);
 94
    Root=CreatTree(1,L);
 95
    updateTree(Root,1,L,1);
 96
    char cmd;
 97
    int s,e,c;
 98
    while (O--)
 99
    
{
100
        scanf(" %c",&cmd);
101
        if(cmd=='C')
102
        
{
103
            scanf("%d%d%d",&s,&e,&c);
104
            if(s>e) swap(s,e);
105
            updateTree(Root,s,e,1<<(c-1));
106
        }
107
        else
108
        
{
109
            cnt=0;
110
            scanf("%d%d",&s,&e);
111
            if(s>e) swap(s,e);
112
            Query(Root,s,e,cnt);
113
            printf("%d\n",cal(cnt));
114
        }
115
    }
116
}