Count Color

题目:Count Color(计数颜色)
poj题号:2777

描述:
选择问题解决和程序设计作为可选课程,您需要解决各种问题。在这里,我们得到一个新的问题。长度为L厘米的长板,L为正整数,所以我们可以将板均匀分为L段,从左到右分别为1,2,… L,分别为1厘米长。现在我们必须为板子着色 - 只有一个颜色的一个部分。我们可以在板上执行以下两个操作:

1.“CAB C”将板A从段A到段B的颜色C.
2.“PA B”输出段A和段B之间绘制的不同颜色的数量(包括)。

在我们的日常生活中,我们有很少的话来描述一种颜色(红色,绿色,蓝色,黄色…),所以你可以假设不同颜色T的总数非常小。为了简单起见,我们将颜色的名称表示为颜色1,颜色2,…颜色T.起初,板子被画成颜色1.现在剩下的问题留给你。

输入

第一行输入包含L(1 <= L <= 100000),T(1 <= T <= 30)和O(1 <= 0 <= 100000)。这里O表示操作次数。在O行之后,每个包含“CAB C”或“PA B”(这里A,B,C是整数,A可以大于B)。
产量

输出结果输出操作顺序,每行包含一个数字。
样品输入

2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2
样品输出

2
1

题解:
首先,题目意思是说,有一块长度为L的木板,一开始上面全为颜色1;形象点就是给定一个长度为N(N <= 100000)的数列L,紧接着O (O <= 100000)条操作,操作形式有两种:
1. “C A B C” 将A到B的数都染成C这种颜色。
2. “P A B” 输出A和B之间不同颜色的数目。

首先,很明显的区间更新和查询前缀和,显然线段树嘛。这题的颜色数量最多30个,我们可以巧妙得利用二进制位运算来存储。因此可以用int类型表示当前区间的颜色数量(对应二进制的每一位储存一种颜色的信息,int最大32位)。 举个例子:左儿子为6(二进制为110)表示有两种颜色,右儿子1(二进制为1)表示有一种颜色,,那么父亲节点就是两个儿子的二进制数的位或7(二进制为111),代表有三种颜色。

这是样例中第一次输出的情况
Count Color_第1张图片

这是第二次输出时的情况
Count Color_第2张图片
ps:图中数字均为二进制

看到这还是懵逼?
具体看代码。

#include
#include
using namespace std;
int n,m,l,tt,o,num=0,ans=0,x,y,value,k;
char deter[2];
struct hh
{
    int l,r;
    int ls,rs,f;//ls为左儿子坐标,rs为右儿子坐标 
    int pd;
    int state;//颜色数量 
}t[300010];
void build(int l,int r)//建树 
{
    num++;
    int i=num;
    t[i].l=l;
    t[i].r=r;
    if(l!=r)
    {
       t[num+1].f=i;
       t[i].ls=num+1;
       build(l,(l+r)/2);

       t[num+1].f=i;
       t[i].rs=num+1;
       build(((l+r)/2+1),r);


    }
   t[i].state=1;
}
void update(int i,int l,int r,int v)//更新 
{
    if(t[i].l==l&&t[i].r==r)//如果全部覆盖,标记一下,直接更新颜色信息 
    {
        t[i].pd=1;//标记 
        t[i].state=1<<(v-1);//位运算求情况数 ,不同的颜色用2进制不同位上的1表示 
        return ;
    }
    if(t[i].pd)//说明是完全覆盖的,更新孩子节点的信息,以免信息没了
    {
        t[i].pd=0;//取消标记 
        t[t[i].ls].pd=t[t[i].rs].pd=1;//把信息传给儿子节点。 
        t[t[i].ls].state=t[t[i].rs].state=t[i].state;//传信息 
    }
    int z=(t[i].l+t[i].r)/2;//各种更新操作 
    if(r<=z) update(t[i].ls,l,r,v);
    else if(l>z) update(t[i].rs,l,r,v);
    else {
        update(t[i].ls,l,z,v);
        update(t[i].rs,z+1,r,v);
    }
    t[i].state=t[t[i].ls].state|t[t[i].rs].state;//最后父节点的颜色数量等于塔两个儿子节点的位或 
}
int query(int i,int l,int r)//求区间[l,r]的颜色数量情况  
{
    if(t[i].l==l&&t[i].r==r)//说明是全覆盖的直接返回状态值即可 
    {
        return t[i].state;
    }
    if(t[i].pd==1)//说明这区间只有一种颜色,直接返回状态值 
    return t[i].state;
    int z=(t[i].l+t[i].r)/2;//剩下你懂的,套路 
    if(r<=z) return query(t[i].ls,l,r);
    else if(l>z) return query(t[i].rs,l,r);
    else 
    {
        return query(t[i].ls,l,z)|query(t[i].rs,z+1,r);
    }
}

int main()
{scanf("%d%d%d",&l,&tt,&o);
        build(1,l);//建树 
    for(int i=1;i<=o;i++)
    {
         scanf("%s",deter);
        if(deter[0]=='C')
        {
        scanf("%d%d%d",&x,&y,&value);
            if(x>y)//考虑大小相反的情况 
            {
                int c=x;
                x=y;
                y=c;
            }
            update(1,x,y,value);//更新 
        }
        else if(deter[0]=='P')
        {
            scanf("%d%d",&x,&y);
            if(x>y)
            {
                int c=x;
                x=y;
                y=c;
            }
            k=query(1,x,y);//求[x,y]区间的数量总和 
            ans=0;
            for(int j=0;j//开始循环看看每一种颜色在不在(因为是用二进制表示,所以要一个一个找) 
            {
                if(k&(1<//这里可能会懵逼,解释一下,(1<
                {
                    ans++;
                }
            }
            printf("%d\n",ans);//输出 
        }
    }
    return 0;
}

你可能感兴趣的:(题目)