二叉链式结构
struct node
{
int ld,rd;
node *lc,*rc;
keytype key;
}
顺序存储结构
struct Node//节点i的左右孩子分别在2*i和2*i+1中
{
int ld,rd;
keytype key;
}Tree[Maxsize];
线段树操作的实现(顺序存储)
建立空线段树
void buildtree(int i,int a,int b)
{
Tree[i].ld = a;
Tree[i].rd = b;
if(b-a == 1) return;
buildtree(i*2,a,(a+b)/2);
buildtree(i*2+1,(a+b)/2,b);
return;
}
插入区间
void insert(int i,int a,int b,keytype key)
{
if(Tree[i].key == 0){//未被完全覆盖
int m = (Tree[i].ld+Tree[i].rd)/2;
if(a==Tree[i].ld && b==Tree[i].rd){
//根据key处理Tree[i].key
return;
}
else if(b<=m) insert(i*2,a,b,key);//左边
else if(a>=m) insert(i*2+1,a,b,key);//右边
else{//一分为二
insert(i*2,a,m,key);
insert(i*2+1,m,b,key);
}
}
}
查找区间
keytype search(int i,int a,int b)
{
keytype res;
if(a<=Tree[i].ld && Tree[i].rd <=b){//完全覆盖
//根据Tree[i],key处理res
return res;
}
if(a<(Tree[i].ld+Tree[i].rd)/2) search(2*i,a,b);
if(b>(Tree[i].ld+Tree[i].rd)/2) search(2*i+1,a,b);
return res;
}
删除区间
void delete(int i,int a,int b)
{
int m = (Tree[i].ld+Tree[i].rd)/2;
if(a<=Tree[i].ld && Tree[i],rd<=b)
{
//对Tree[i].key做相应的处理
return;
}
else if(a<m) delete(i*2,a,b);
else if(b>m) delete(i*2+1,a,b);
else{
delete(i*2,a,m);
delete(i*2+1,m,b);
}
}
计算区间长度
int QLen(i)
{
if(Tree[i].key = 1) return Tree[i].rd-Tree[i].ld;//完全覆盖
else if(Tree[i].rd-Tree[i].ld == 1) return 0;//叶子
else return QLen(i*2)+Q(i*2+1);
}
相关例题:
zoj1610 Count the Colors
#include <iostream>
#include<string.h>
using namespace std;
#define maxn 8000
#define NOCOLOR -1
#define DIVCOLOR -2
//线段树节点
struct node
{
int left;
int right;
int key; //存储颜色
} Tree[maxn * 3];//注意节点要存多几倍的
//初始化线段树
void Init(int l, int r, int index)
{
Tree[index].left = l;
Tree[index].right = r;
if (Tree[index].left + 1 == Tree[index].right)//7到叶子节点了
return;
int mid = (l + r) / 2;
Init(l, mid, 2 * index);
Init(mid, r, 2 * index + 1);
}
void Insert(int l, int r, int index, int key)
{
//相同颜色则不做插入
if (Tree[index].key == key)
return;
//可以完全覆盖
if (Tree[index].left == l && Tree[index].right == r)
{
Tree[index].key = key;
return;
}
//不能完全覆盖
//如果不是已经分好的,要么就是这一个段全都是一个颜色,要么就是没颜色
if (Tree[index].key != DIVCOLOR)//如果这一段是有多种颜色的话
{//下推到子节点
Tree[2 * index].key = Tree[index].key;
Tree[2 * index + 1].key = Tree[index].key;
}
Tree[index].key = DIVCOLOR;//让这个节点标记有多种颜色
int mid = (Tree[index].left + Tree[index].right) / 2;
if (r <= mid)
{
Insert(l, r, 2 * index, key);
return;
}
if (l >= mid)
{
Insert(l, r, 2 * index + 1, key);
return;
}
Insert(l, mid, 2 * index, key);
Insert(mid, r, 2 * index + 1, key);
}
int colorsave = -1;
int m[maxn + 5];
void countcolor(int index) //搜索Tree[index]有多少种颜色
{
if (Tree[index].key == DIVCOLOR) //里面有多种颜色
{//递归遍历子树
countcolor(2 * index);
countcolor(2 * index + 1);
}
if (Tree[index].key == NOCOLOR) //没有颜色
{
colorsave = NOCOLOR;
}
if (Tree[index].key >= 0) //有同一种颜色
{
if (Tree[index].key != colorsave) //如果和前面的颜色不同,则要记录
{
m[Tree[index].key]++;
colorsave = Tree[index].key;
}
return; //和前面的颜色相同,则直接return
}
}
int main()
{
int n;
int l, r, k;
//建树
Init(0, maxn, 1);
while (cin >> n)
{
//初始树
Tree[1].key = NOCOLOR;
while (n--)
{
cin >> l >> r >> k;
Insert(l, r, 1, k); //插入树
}
memset(m, 0, sizeof(m)); //初始化m列表
//计算颜色数量
countcolor(1);
//输出结果
for (int i = 0; i <= maxn; i++)
{
if (m[i])
{
cout << i << " " << m[i] << endl;
}
}
cout << endl;
}
return 0;
}
zoj2451 Minimizing maximizer
题意:给一个长度为n的区间,m条线段序列(也就是m个sorter),找出这个序列的一个最短子序列(最少的sorter数),使得区间完全被覆盖(使整个区间有序)
#include<iostream>
#include<stdio.h>
#include<memory.h>
using namespace std;
struct node{
int step;
int left,right;
}tree[130000];
const int MAX=0x7ffffff;
int N,M;
//初始化线段树,让每个节点的step都为无穷大
void Init(int i,int a,int b)
{
tree[i].step = MAX;
tree[i].left = a;
tree[i].right = b;
if(a==b) return;//到叶子节点了
int mid = (a+b)/2;
Init(2*i,a,mid);
Init(2*i+1,mid+1,b);
}
//插入,从tree的第i个节点往end节点中插入s
void Insert(int i,int end,int s)
{
if(s<tree[i].step){//从根往叶子更新step
tree[i].step = s;
}
if(tree[i].left==tree[i].right) return;//到叶子节点了
//二分查找end值位置,沿路都进行赋值
int mid = (tree[i].left+tree[i].right)/2;
if(end<=mid){
Insert(2*i,end,s);
}else{
Insert(2*i+1,end,s);
}
}
//从第i个节点搜索a到b最少需要多少根水管
int Count(int i,int a,int b)
{
if(a==tree[i].left && b==tree[i].right){
return tree[i].step;
}
int mid=(tree[i].left+tree[i].right)/2;
if(b<=mid){
return Count(2*i,a,b);
}
if(a>mid){
return Count(2*i+1,a,b);
}
//左右两边看这个区间段内所需step最小的
int t1,t2;
t1 = Count(2*i,a,mid);
t2 = Count(2*i+1,mid+1,b);
if(t1<t2){
return t1;
}else{
return t2;
}
}
void proc()
{
int t,x,y;
Init(1,1,N);//建树
Insert(1,1,0);//从第i个节点开始搜索值为1节点,赋值step为0
for(int i=0;i<M;i++){
scanf("%d %d",&x,&y);
if(x<y){
t = Count(1,x,y-1);//计算前面x~y-1最小的step
Insert(1,y,t+1);//从第i个节点开始搜索值为y节点,赋值t+1
}
}
printf("%d\n",Count(1,N,N));
}
int main(){
while(scanf("%d %d",&N,&M)!=EOF){
proc();
}
return 0;
}
poj2777 Count color
题意:一条很长(L)的画板,有T种颜色,O个操作;每次操作将一个区间刷成一种颜色,或者查询一个区间内所含的颜色数。
#include<iostream>
#include<stdio.h>
using namespace std;
#define maxn 300010
struct node
{
int lc,rc;
int state; //用二进制的每一位中的1的个数来标记颜色的个数
int id; //标记状态看是否被完全覆盖
}tree[maxn];
//建树
void build(int i, int l, int r)
{
int mid;
tree[i].lc=l;
tree[i].rc=r;
if(l==r)
return;
mid=(l+r)>>1;
build(2*i, l, mid);
build(2*i+1, mid+1,r);
}
//从s到t涂上颜色value,从T节点开始查找
void insert(int s,int t,int T,int value)
{
int mid;
//说明涂色范围正好完全覆盖T所包含的区域
if(tree[T].lc==s&&tree[T].rc==t)
{
tree[T].id=1; //标记为完全覆盖
tree[T].state=1<<(value-1); //不同的颜色用2进制不同位上的1表示
return;
}
//id!=0说明T是完全覆盖的,且现给定区域不能完全涵盖该节点的范围
if(tree[T].id)
{
tree[T].id=0;//修改T节点id为0
tree[2*T].id=tree[2*T+1].id=1;//修改孩子节点id为1(传递)
tree[2*T].state=tree[2*T+1].state=tree[T].state;//T节点原只有一种颜色,且完全覆盖,则传递这个只有一个颜色的status
}
mid=(tree[T].lc+tree[T].rc)/2;
if(t<=mid)// 插入的范围全部在左子树
insert(s,t,2*T,value);
else if(s>mid)// 插入的范围全部在右子树
insert(s,t,2*T+1,value);
else// 分别插入
{
insert(s,mid,2*T,value);
insert(mid+1,t,2*T+1,value);
}
//更新一路下来节点T的信息,经过的点都加上存在新增颜色的信息
tree[T].state=(tree[2*T].state)|(tree[2*T+1].state);//等同于把颜色数相加(并集)
//剪枝,合并节点信息
if(tree[2*T].id&&tree[2*T+1].id&&tree[2*T].state==tree[2*T+1].state)
tree[T].id=1;
}
//从T节点开始查询区间s到t颜色状态(01串)
int qurry(int s,int t,int T)
{
if(tree[T].lc==s&&tree[T].rc==t)//如果完全覆盖
return tree[T].state;
if(tree[T].id)//如果没有完全覆盖,但是更大范围的颜色数也只有1,就不用再往下遍历了
return tree[T].state;
int mid=(tree[T].lc+tree[T].rc)/2;
if(t<=mid)
return qurry(s,t,2*T);
else if(s>mid)
return qurry(s,t,2*T+1);
else
return qurry(s,mid,2*T)|qurry(mid+1,t,2*T+1);
}
int main()
{
int i,j,k,color,t,num,len,m;
char cmd[2];
while(scanf("%d%d%d",&len,&color,&m)!=EOF)
{
build(1,1,len);//建线段树[1,len]
tree[1].state=1;//初始根节点状态为只涂了1这个颜色
tree[1].id=1;//且只有一种颜色
while(m--)//进行m次事件
{
scanf("%s",cmd);
if(cmd[0]=='C')//进行涂色
{
scanf("%d%d%d",&i,&j,&k);
if(i>j)//特判ij顺序颠倒情况
{
t=i; i=j; j=t;
}
insert(i,j,1,k);//从根节点1开始搜索,往[i,j]中涂k色
}
else//进行询问
{
scanf("%d%d",&i,&j);
if(i>j)//特判ij顺序颠倒情况
{
t=i; i=j; j=t;
}
k=qurry(i,j,1);//从根节点1开始搜索[i,j]区间内涂色情况
num=0;//数数
for(i=0;i<color;++i)
if(k&(1<<i))
++num;
printf("%d\n",num);
}
}
}
return 0;
}