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 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

 
题目大意:有一段长度为L的板,分为L段。对这些段进行一些列染色询问操作如下:
C x y z:把x,y之间的段染成z颜色。
P x y:询问x,y之间有多少种颜色。
L最大100000,询问最多100000,但颜色最多30种。
 
观察颜色,不大于30,也就是说可以用longint储存下来颜色出现情况。这样,就可以用线段树维护了。
每一个线段树上的节点开一个a数组记录它所代表的段上面颜色的出现情况。再开一个p数组,用来标记,p[i]为true则一整段为一种颜色,下次修改询问的时候往下推标记,这样可以省很多时间。
对于修改时候的更新,只要把两个儿子的a值进行or运算赋到当前节点的a值上即可。
对于询问,也用or运算得到颜色出现情况color,最后计数1的个数。这里可以用树状数组里面lowbit的求法,用一个while不断地从color中减去lowbit(color)直到color为0。这样减了几次,就有几种不同的颜色。
 
这题RE了好多次,结果再看题目,竟然有一句‘and A may be larger than B’!!!瞬间无语了。。。每一次读入还要判断头尾端点的大小关系,进行调换。。。有种被阴的感觉。。。还有,每一段的初始颜色是1。
 
AC CODE
 
program pku_2777;
var a:array[1..600000] of longint;
      p:array[1..600000] of boolean;
      color,s,t,z,i,n,o:longint;
      command:char;
//============================================================================
procedure change(be,en,now:longint);
var mid:longint;
begin
  if (be>=s) and (en<=t) then
  begin
      a[now]:=color;
      p[now]:=true; exit;      //打标记。
  end;
  if p[now] then      //推标记。
  begin
      a[now shl 1]:=a[now]; p[now shl 1]:=true;
      a[now shl 1+1]:=a[now]; p[now shl 1+1]:=true;
      p[now]:=false;      //一定记得要取消标记。
  end;
  mid:=(be+en) shr 1;
  if t<=mid then change(be,mid,now shl 1) else
  if s>mid then change(mid+1,en,now shl 1+1) else
  begin
      change(be,mid,now shl 1);
      change(mid+1,en,now shl 1+1);
  end;
  a[now]:=a[now shl 1] or a[now shl 1+1];      //递归回来要更新a值。
end;
//============================================================================
procedure ask(be,en,now:longint);
var mid:longint;
begin
  if (be>=s) and (en<=t) then
  begin
      color:=color or a[now];      //在color中加入当前段的颜色。or很好用的说。。。
      exit;
  end;
  if p[now] then
  begin
      a[now shl 1]:=a[now]; p[now shl 1]:=true;
      a[now shl 1+1]:=a[now]; p[now shl 1+1]:=true;
      p[now]:=false;
  end;
  mid:=(be+en) shr 1;
  if t<=mid then ask(be,mid,now shl 1) else
  if s>mid then ask(mid+1,en,now shl 1+1) else
  begin
      ask(be,mid,now shl 1);
      ask(mid+1,en,now shl 1+1);
  end;
end;
//============================================================================
begin
  fillchar(p,sizeof(p),0);
  readln(n,t,o);
  a[1]:=1; p[1]:=true;      //本来想for一遍或调用change的,但是标记这么好用的东西不用白不用。
  for i:=1 to o do
  begin
      read(command);
      if command='C' then
      begin
          readln(s,t,z);
          if s>t then      //调换头尾端点,用color是因为懒得开空间了。。。
          begin
              color:=s; s:=t; t:=color;
          end;
          color:=1 shl (z-1);  //把颜色转成10进制。
          change(1,n,1);
      end else
      begin
          readln(s,t);
          if s>t then
          begin
              color:=s; s:=t; t:=color;
          end;
          color:=0; ask(1,n,1);
          z:=color; color:=0;
          while z>0 do      //树状数组的lowbit求法。
          begin
              z:=z-z and (-z);
              inc(color);
          end; writeln(color);
      end;
  end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值