您的位置:首页 > 其它

线段树 POJ2777 Count Color 解题报告

2015-08-29 07:07 288 查看
Count Color

Time Limit: 1000MS Memory Limit: 65536K

Total Submissions: 39860 Accepted: 12024

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

POJ Monthly--2006.03.26,dodo

 思路:参考-----http://www.cnblogs.com/dongsheng/archive/2012/08/08/2628845.html

      一看到数据量就可以首先确定是线段树了,经典的区间染色问题,涉及到区间的

 更新和询问,和pku  类似,巧妙运用lazy思想。就是每次更新区间段的时候延迟

  更新,只是在完全覆盖的区间打上一个lazy标记。这题的询问是求区间段中不同颜色的

  数量,因为颜色数不多只有30种,可以巧妙运用二进制位运算,用一个int就可以表示

  当前区间段的颜色情况。

      再来谈谈lazy思想。做了这么多的线段树,应该总结一下,lazy是一个很经典的思

  想。所谓lazy,就是懒惰,每次不想做太多,只要插入的区间完全覆盖了当前结点所管

  理的区间就不再往下做了,在当前结点上打上一个lazy标记,然后直接返回。下次如果

  遇到当前结点有lazy标记的话,直接传递给两个儿子,自己的标记清空。这样做肯定是

  正确的。我们以染色为例,可以这样想,如果当前结点和它的子孙都有lazy标记的话,

  必定是子孙的先标记,因为如果是自己先标记,那么在访问子孙的时候,必定会将自己

  的标记下传给儿子,而自己的标记必定会清空,那么lazy标记也就不存在了。所以可以

  肯定,当前的lazy标记必定覆盖了子孙的,所以直接下传即可,不需要做任何判断。当

  然,这是染色问题,是直接赋值的。

  */

  //这是仿照别人写的AC代码

  #include<stdio.h>

  struct node

  {

      int lc,rc;

      int state;    //用二进制的每一位中的1的个数来标记颜色的个数

      int flag;       //标记状态看是否被完全覆盖

  }tree[200000];

  void build(int s,int t,int T)//口诀:赋值,判断(叶子),递归,如果某些参数还需要叶子点计算得到,还需要(回溯)更新这些参数

  {

      tree[T].lc=s,tree[T].rc=t;//赋值

      if(s==t) return ;//判断

      int mid=(s+t)>>1;//递归

      build(s,mid,T<<1);

      build(mid+1,t,(T<<1)|1);

  }

  void insert(int left,int right,int Node,int value)       //从left到right涂上颜色value,从Node节点开始查找

  {

      if(tree[Node].lc==left&&tree[Node].rc==right)

      {//说明涂色范围正好完全覆盖Node所包含的区域,直接更新颜色信息,下边的节点不用管。以后访问的时候再将Node的信息传给儿子。

          tree[Node].flag=1;     //标记为完全覆盖

          tree[Node].state=1<<(value-1);     //2的value-1次方。不同的颜色用进制不同位上的1表示

          return ;

      }

      if(tree[Node].flag)      //说明Node是完全覆盖的,更新Node孩子节点的信息,才能进一步染色

      {

          tree[Node].flag=0;

          tree[Node<<1].flag=tree[(Node<<1)|1].flag=1;

          tree[Node<<1].state=tree[(Node<<1)|1].state=tree[Node].state;

      }

      //递归对左右儿子进行染色

      int mid=(tree[Node].lc+tree[Node].rc)>>1;

      if(right<=mid) insert(left,right,Node<<1,value);

      else if(left>mid) insert(left,right,(Node<<1)|1,value);

      else

      {

          insert(left,mid,Node<<1,value);

          insert(mid+1,right,(Node<<1)|1,value);

      }

      //上面完成染色后,回溯更新Node颜色数目和flag

      tree[Node].state=(tree[Node<<1].state)|(tree[Node<<1|1].state);// 等同于把颜色数相加

      if(tree[Node<<1].flag&&tree[(Node<<1)|1].flag&&tree[Node<<1].state==tree[(Node<<1)|1].state)

      tree[Node].flag=1;

  }

  int qurry(int left,int right,int Node)          //从Node节点开始查询区间left到right颜色的个数

  {

      if(tree[Node].lc==left&&tree[Node].rc==right) return tree[Node].state;

      if(tree[Node].flag)       //减枝----说明Node是全覆盖的直接返回状态值即可

          return tree[Node].state;

     //递归查询

     int  mid=(tree[Node].lc+tree[Node].rc)>>1;

     if(right<=mid) return qurry(left,right,Node<<1);

     else if(left>mid) return qurry(left,right,(Node<<1)|1);

     else return qurry(left,mid,Node<<1)|qurry(mid+1,right,(Node<<1)|1);

 }

 int main()

 {

      freopen("output.txt","w",stdout);

     freopen("input.txt","r",stdin);

     int i,j,k,color,t,num,len,m;

     char cmd[2];

     while(scanf("%d%d%d",&len,&color,&m)!=EOF)

     {

         build(1,len,1);

         tree[1].state=1;

         tree[1].flag=1;

         while(m--)

         {

             scanf("%s",cmd);

             if(cmd[0]=='C')

             {

                 scanf("%d%d%d",&i,&j,&k);

                 if(i>j)

                 {

                     t=i;

                     i=j;

                     j=t;

                 }

                 insert(i,j,1,k);

             }

             else

             {

                 scanf("%d%d",&i,&j);

                 if(i>j)

                 {

                     t=i;

                     i=j;

                     j=t;

                 }

                 k=qurry(i,j,1);

                 num=0;

                 for(i=0;i<color;++i)

                     if(k&(1<<i))

                         ++num;

                 printf("%d\n",num);

             }

         }

     }

    return 0;

 }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息