题目来源: 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
5using namespace std;
6
7struct Seg_Tree //树节点的数据结构
8{
9 Seg_Tree *leftPtr,*rightPtr; //左右子树的指针
10 int left,right; //节点的表示范围
11 int color; //节点的color
12};
13
14int L,T,O,cnt;
15int numOfTree;
16Seg_Tree tree[MAXM*10],*Root; //所有的节点和根节点
17
18Seg_Tree* creatNode() //创建新节点
19{
20 Seg_Tree *p= &tree[numOfTree++];
21 memset(p,0,sizeof(Seg_Tree));
22 return p;
23}
24
25Seg_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
38bool odd(int n) //判断这个颜色是否为单色
39{
40 return (n&(n-1))==0;
41}
42
43void 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
62void 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
79int 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
90int 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}