题目: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),代表有三种颜色。
看到这还是懵逼?
具体看代码。
#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;
}