poj 2777 Count Color (线段树 + 覆盖标记)

poj 2777   Count Color  (线段树 + 覆盖标记)


Count Color
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 38676 Accepted: 11625

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 divide the board into L segments, and they are labeled by 1, 2, ... L from left to right, each is 1 centimeter long. Now we have to color the board - one segment with only one color. We can do following two operations on the board:

1. "C A B C" Color the board from segment A to segment B with color C.
2. "P A B" Output the number of different colors painted between segment A and segment B (including).

In our daily life, we have very few words to describe a color (red, green, blue, yellow…), so you may assume that the total number of different colors T is very small. To make it simple, we express the names of colors as color 1, color 2, ... color T. At the beginning, the board was painted in color 1. Now the rest of problem is left to your.

Input

First line of input contains L (1 <= L <= 100000), T (1 <= T <= 30) and O (1 <= O <= 100000). Here O denotes the number of operations. Following O lines, each contains "C A B C" or "P A B" (here A, B, C are integers, and A may be larger than B) as an operation defined previously.

Output

Ouput results of the output operation in order, each line contains a number.

Sample Input

2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2

Sample Output

2
1

Source



题意:类似为给定一段直线般的墙,让你给墙上色,总共有30种颜色,本来的颜色是1,颜色可以进行覆盖,首先是三个数字L, T, O
L表示墙的长度,T表示有多少种颜色,O表示后面有多少执行语句
如果字符是C,后面跟三个数字1,2,3,表明从1到2的范围内,涂上3这种颜色。如果是P,后面跟两个数字1,2,问从1到2的范围内,能看见多少种颜色。



题解:这道题其实和poj 2528很类似,只是多次询问范围上的颜色,我们可以建立线段树,通过覆盖点的方法来标记,防止每次都改变所有有关
点而造成的超时,我们通过每次从上到下查找到所要求的范围进行上色,只要找到范围的结点给予颜色的标记就可以,不用精确到每个点。
上色时,我们可以通过Updata来二分查找是来更新点的覆盖标记,要是上面的点范围太大,不合适,只要将范围结点赋值为-1,
然后让它的左右孩子赋值为它的值就可以了,接着想下找,找到合适的范围再赋值相应的颜色,这样来通过改变范围的值,可以节省很多时间。



#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
#include <iostream>

using namespace std;

#define lson l, m, rt * 2                 //方便下面的使用
#define rson m + 1, r, rt * 2 + 1

#define maxn 100005

int tree[maxn << 2];                    //开辟一课二叉树,用其数量的4倍,是比较合适的大小,不会太大,有可以防止特殊情况的发生
int flag[40];                          //这里不能无所谓的写maxn,之后的循环,最好减少耗时
int ant;

void BuildTree (int l, int r, int rt)        //给予所有结点1,当作是初始颜色
{
	int m = (r + l) / 2;

	if (r == l)
	{
		tree[rt] = 1;
		return ;
	}
	BuildTree (lson);
	BuildTree (rson);
	tree[rt] = 1;
}


void Updata (int l, int r, int rt, int L, int R, int v)       //覆盖操作,由于一个个改变值太复杂,就进行标记,等到用的时候来进行覆盖
{
	int m = (l + r) / 2;

	if (L <= l && R >= r)
	{
		tree[rt] = v;
		return ;
	}
	if (tree[rt] != -1)         //这就是覆盖,在这个结点下面的孩子都是和它一种颜色,要是有新的颜色,只要覆盖比它范围要小的几个点
	{                            //那就通过m = (l + r) / 2;这样的方法下去,找到一个合适的范围进行覆盖标记
		tree[rt * 2] = tree[rt * 2 + 1] = tree[rt];
		tree[rt] = -1;
	}
	
	if (L <= m)
		Updata (lson, L, R, v);
	if (R > m)
		Updata (rson, L, R, v);
}

void Query (int L, int R, int l, int r, int rt)
{
	int m = (l + r) / 2;            //这一步就不用进行范围的确定了,要是tree[rt] != -1,那就表明这个之下的范围都是一种颜色
                                   //就直接进行颜色种类的相加就好了
	if (tree[rt] != -1)
	{
		if (flag[tree[rt]] == 0)
		{
			flag[tree[rt]] = 1;
			ant++;
		}
		return;                //找到了tree[rt] != -1就return;要是没有就折半查找下去,覆盖标记也不用更新,除了颜色种类的标记要更新
	}
	if (L <= m)
		Query (L, R, lson);
	if (R > m)
		Query (L, R, rson);
}

int main ()
{
	int L, T, O, i, r, l, v, temp;
	char ch;

	while (scanf ("%d%d%d", &L, &T, &O) != EOF)
	{
		BuildTree (1, L, 1);
		for (i = 0; i < O; ++i)
		{
			getchar ();
			scanf ("%c", &ch);
			if (ch == 'C')
			{
				scanf ("%d%d%d", &l, &r, &v);
				if (l > r)                  //注意,给出的范围并没有说过是前面大于后面
				{
					temp = l;
					l = r; 
					r = temp;
				}
				Updata (1, L, 1, l, r, v);
			}
			else
			{
				ant = 0;
				memset (flag, 0, sizeof (flag));        //因为是每次询问一次范围,于是要每次使颜色种类标价更新为0
				scanf ("%d%d", &l, &r);
				if (l > r)                 //注意,给出的范围并没有说过是前面大于后面
				{
					temp = l;
					l = r; 
					r = temp;
				}
				Query (l, r, 1, L, 1);
				printf ("%d\n", ant);
			}
		}
	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值