区域染色覆盖问题
假设某大学有一面文化墙,各个学院都可以在上面涂色,要求涂色区域高必须和墙一样,宽度任意但必须是整数(以米为单位)。涂色可以覆盖其他学院的涂色。现在若干个学院涂色之后最终这面墙上能看见多少种颜色。
这就是简单的染色问题了。当然有很多种不同的说法,但整体上离不开这两个问题:1.区域覆盖。2.多种染色方式。既然是区域操作,那么用线段树是比较合理了。用数组也可以,但是数据规模稍微大一点就会TLE。
当然,跟染色问题一同存在的还有就是离散化,很多博客也是将这两者一起来写的,并且主要是离散化。但我个人感觉染色问题对理解线段树很有帮助(当然还是太菜了= =大佬们都认为难点是线段树的离散化),所以就单独拿出来了。
建树
线段树有多种表现形式,感觉很多人都是拿结构体写的,博主是用数组写的,其本质还是一样的代码有些不一样而已。
整体上,我们对一段数据建成线段树,那么区域染色的时候就类似与修改区间。而线段树的精髓就是利用lazy数组,可以保证不遍历到子结点就可以获得区域的情况。那么染色也要利用这个性质,对于一段区间,我们想要在上层结点上来表示出来。那么我们可以设置3个变量:-1 表示当前区段有多种颜色,具体有多少种不用管。0表示当前区域未染色。正整数表示当前区域染了单一染色,并且颜色号是这个正整数。
所以除了特殊要求,一般我们用int来表示颜色种类(就算题目是char类型或者string,我们也可以变成int,最多写一个hash,还是整数方便)。那么看下面这个例子:
这是按照上面的要求写的线段树,-1表示当前段有多种颜色,1表示当前段全部是1。这里左边,一个1一个0,而上面结点仍然是-1。这是因为默认把0也当成一种颜色了,可以节省代码,实际在查找的时候我们只用排除0就可以得到正确答案。
总之,这样建立的线段树可以表示区域染色的大致情况。而具体有多少种颜色需要在查询的时候进行操作。
关于建树,因为染色问题,所以有覆盖这种说法,后涂的颜色会覆盖先涂的,所以一般原数据是什么都没有,也就是整个线段树的值全是0。这样初始化代码只用将线段树初始化就行了。即:
void build(){
memset(tree,0,sizeof(tree));
}
// 有这样的情况,底层有规定颜色,那么就不能用memset而是要用for循环
void build(int color){
for(int i = 1;i <= len;i++){
tree[i] = color;
}
}
不排除有刚开始就有涂色的