poj(2777)——Count Color(lazy思想,成段更新,区间统计)

题目的意思是:

现在我们有l个数,然后标记为1到n,他们的单位长度都是1,然后在每个单位长度的地方我们只能染上一种颜色。

现在有两种操作:

"C A B C"代表给A,B区间都染上C这种颜色。

"P A B" 相当于是询问,需要输出A,B这个区间不同颜色的数量是多少。

一开始我在想要怎么求不同颜色的数量,后来发现题目中说颜色的范围是30种颜色,所以在这里我们就可以进行暴力枚举了。

这个染色问题我一开始没怎么懂,后来在纸上模拟了一下别人的代码,然后就理解了。

首先如果那个节点完全包含那个区间的话,那么我们就把这个节点标记上颜色,不往下更新了,这也是lazy思想,直到下次遇到且颜色与当前要标记的颜色不同时在往下更新。

询问的话,是从最上面开始找,如果发现有一个节点是纯色的话(也就是说它被标记过的话),那么就记录下来,并且可以直接返回了,因为纯色说明下面节点的颜色都与这个节点的颜色相同,因为它包含了下面的节点了。所以最后我们只需要遍历一遍颜色然后再计数就好了。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 111111
struct node{
	int l,r,col;
}tree[maxn*4];
bool cc[55];
void build(int v,int l,int r){
	tree[v].l=l;
	tree[v].r=r;
	tree[v].col=1;
	if(l==r) return ;
	int temp=v<<1;
	int mid=(l+r)>>1;
	build(temp,l,mid);
	build(temp+1,mid+1,r);
}
void update(int v,int l,int r,int color){
	if(tree[v].l==l&&tree[v].r==r){
		tree[v].col=color;
		return;
	}
	if(tree[v].col&&tree[v].col!=color){
		int temp=v<<1;
		tree[temp].col=tree[v].col;
		tree[temp+1].col=tree[v].col;
		tree[v].col=0;
	}
	int mid=(tree[v].l+tree[v].r)>>1;
	int temp=v<<1;
	if(r<=mid) update(temp,l,r,color);
	else if(l>mid) update(temp+1,l,r,color);
	else{
		update(temp,l,mid,color);
		update(temp+1,mid+1,r,color);
	}
}
void query(int v,int l,int r){
 	if(tree[v].col>0){
		cc[tree[v].col]=true;
		return ;
	}
	int mid=(tree[v].l+tree[v].r)>>1;
	int temp=v<<1;
	if(r<=mid) query(temp,l,r);
	else if(l>mid) query(temp+1,l,r);
	else{
		query(temp,l,mid);
		query(temp+1,mid+1,r);
	}
}
int main(){
	int L,T,O;
	char ss[4];
	scanf("%d%d%d",&L,&T,&O);
	build(1,1,L);
	#if 1
	while(O--){
		scanf("%s",ss);
		int a,b,c;
		if(ss[0]=='C'){
			scanf("%d%d%d",&a,&b,&c);
			update(1,a,b,c);
		}
		else if(ss[0]=='P'){
			int ans=0;
			fill(cc,cc+55,false);
			scanf("%d%d",&a,&b);
			query(1,a,b);
			for(int i=1;i<=T;i++){
				if(cc[i]) ans++;
			}
			printf("%d\n",ans);
		}
	}
	#endif
}
/*
8 5 4
C 5 6 1
C 5 5 3
C 6 6 4
P 5 6
*/

思路大致是这样的:

思路:

这个题与poj2528贴海报类似,涂色问题。之前做poj2528时并不知道这个思想,对代码理解的也不透彻。做了这道题以后,对Lazy-Tag思想有了更深的理解了。

当建立好一棵线段树之后,就是往上面涂色。涂色问题可分为以下几个步骤:

1.如果当前区间已经染色,且其颜色和欲染颜色相同,则直接退出(这句可以不要)

2.如果当前区间被欲染色区间完全覆盖,那么当前区间的子区间也被覆盖,那么直接给当前区间染上该颜色直接退出。

3.如果没有被完全覆盖,首先给左右子树染成当前区间的颜色,然后当前区间颜色赋值为混合色为0,再递归染色左右子树。

这样修改被完全覆盖的区间时就可以直接修改而不用遍历左右子树,而对于没有被完全覆盖的区间,先将其颜色传给左右子树,再递归修改,保证了子树颜色的正确性。大大降低了时间复杂度。

对于区间统计,设置mark数组,标记某一颜色是否显现出来。如果一个区间涂上了红色,那么这个区间的任何子区间都涂上了红色,mark标记为1,就不必向下遍历了。当是混合色(为0时)就要继续遍历子树。最后统计mark值为1的个数即可。

推荐一下这个人写的: http://www.2cto.com/kf/201402/277917.html

时间: 2024-08-28 17:58:54

poj(2777)——Count Color(lazy思想,成段更新,区间统计)的相关文章

POJ 2777 Count Color (线段树成段更新+二进制思维)

题目链接:http://poj.org/problem?id=2777 题意是有L个单位长的画板,T种颜色,O个操作.画板初始化为颜色1.操作C讲l到r单位之间的颜色变为c,操作P查询l到r单位之间的颜色有几种. 很明显的线段树成段更新,但是查询却不好弄.经过提醒,发现颜色的种类最多不超过30种,所以我们用二进制的思维解决这个问题,颜色1可以用二进制的1表示,同理,颜色2用二进制的10表示,3用100,....假设有一个区间有颜色2和颜色3,那么区间的值为二进制的110(十进制为6).那我们就把

poj 2777 Count Color【线段树段更新】

题目:poj 2777 Count Color 题意:给出一段1 * n 的栅栏,有两种操作,第一种:把 l -- r 全部染成同一颜色t,第二种,查询 l---r 一共有多少种颜色. 分类:线段树 分析:我们可以给每个节点加一个标记,标记当前节点是否只有一种颜色,然后对只有一种颜色的节点如果要染色的话,那么他会变成几种颜色的,这时候记得向下更新一次就好,统计的时候统计节点有单个颜色的颜色就好. 代码: #include <cstdio> #include <cstring> #i

POJ训练计划2777_Count Color(线段树/成段更新/区间染色)

解题报告 题意: 对线段染色,询问线段区间的颜色种数. 思路: 本来直接在线段树上染色,lz标记颜色.每次查询的话访问线段树,求出颜色种数.结果超时了,最坏的情况下,染色可以染到叶子节点. 换成存下区间的颜色种数,这样每次查询就不用找到叶子节点了,用按位或来处理颜色种数. #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace

POJ 2777 Count Color(线段树)

POJ 2777 Count Color 题目链接 就一个线段树,颜色二进制表示就可以,成段更新成段查询延迟操作 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define lson(x) ((x<<1)+1) #define rson(x) ((x<<1)+2) const int N = 100005; struct No

POJ 2777 Count Color (线段树+位运算)

题意很简单了,对一个区间有两种操作: 1. "C A B C" Color the board from segment A to segment B with color C. //A~B涂上颜色C 2. "P A B" Output the number of different colors painted between segment A and segment B (including). //输出A~B间颜色的种类数 题目链接:http://poj.o

poj 2777 Count Color(线段树区间修改)

题目链接:http://poj.org/problem?id=2777 题目意思:就是问你在询问的区间里有几种不同的颜色 思路:这题和一般的区间修改差不多,但是唯一不同的就是我们要怎么计算有种颜色,所以这时候我们就需要把延时标记赋予不同的意义,当某段区间有多种颜色时就赋值为-1,当为一种颜色时就把它赋值为这个颜色的号数.这儿我们要怎么统计询问区间不同的颜色数叻,为了不重复计算同一种颜色,那么我们就需要用一个数组来标记计算过的颜色,当我们下次遇到时就不需要再次计算了.... 代码核心处就在计数那儿

POJ 2777 Count Color(线段树)

题目地址:POJ 2777 我去..延迟标记写错了.标记到了叶子节点上....这根本就没延迟嘛...怪不得一直TLE... 这题就是利用二进制来标记颜色的种类.然后利用或|这个符号来统计每个区间不同颜色种数. 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h

POJ 2777 Count Color (线段树区间更新加查询)

Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem. There is a very long board with length L centimeter, L is a positive integer, so we can evenly d

NYOJ 1068 ST(线段树之 成段更新+区间求和)

ST 时间限制:1000 ms  |  内存限制:65535 KB 难度:1 描述 "麻雀"lengdan用随机数生成了后台数据,但是笨笨的他被妹纸的问题给难住了... 已知lengdan生成了N(1=<N<=10005)个随机整数,妹子对这些数可能有以下几种操作或询问: 1,A a b c 表示给区间a到b内每个数都加上c: 2,S a b  表示输出区间a到b内的和: 3,Q a b 表示区间a到b内的奇数的个数: 为了使妹纸不口渴,所以我们决定妹纸的询问次数少一点,即