继续线段树专题练习。。。
今天似乎对lazy-tag有一点点感觉了。
当我们更新一个区间时,不必更新它的子区间,等到需要用到时再更新。也就避免了每次都更新到底。从而降低时间复杂度。
这个题用的是位运算,时间上较快。
由于颜色至多只有30种,于是我们可以用一个int型的整数来表示一种颜色。
如000…01表示color1, 000…10表示color2,以此类推,100…00表示color30
对于非叶子节点rt,记录的是左孩子和右孩子节点异或的值。亦即:sum[rt]=sum[lson] | sum[rson];
比如左孩子color1 为000…01,右孩子color2 为000…10,异或后的值为000…11。此时父节点的颜色数为2.。就是二进制中1的个数。
对于一个区间,只需求出完美覆盖该区间的各节点异或后的值,再输出该值二进制中1的个数即可。
下面是我的代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define mid ((l+r)>>1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int MAXL=100001;
int sum[MAXL<<2],col[MAXL<<2];
int count(int p)
{ //计算p的二进制中1的个数
int cnt=0;
while(p){
if(p%2) cnt++;
p>>=1;
}
return cnt;
}
void PushUp(int rt)
{
sum[rt]=sum[rt<<1]|sum[rt<<1|1];
}
void PushDown(int rt)
{
if(col[rt]){
col[rt<<1]=col[rt<<1|1]=col[rt]; //标记左右孩子节点的颜色
sum[rt<<1]=sum[rt<<1|1]=1<<(col[rt]-1); //标记左右孩子节点的颜色值
col[rt]=0;
}
}
void build(int l,int r,int rt)
{
col[rt]=0;
if(l==r){
sum[rt]=1;
return ;
}
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int val,int l,int r,int rt)
{
if(L<=l && r<=R){
col[rt]=val;
sum[rt]=1<<(val-1);
return ;
}
PushDown(rt);
if(L<=mid) update(L,R,val,lson);
if(R>mid) update(L,R,val,rson);
PushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l && r<=R)
return sum[rt];
PushDown(rt);
int ret=0;
if(L<=mid) ret|=query(L,R,lson);
if(R>mid) ret|=query(L,R,rson);
return ret;
}
int main()
{
int L,T,O;
int a,b,c;
char op[2];
cin>>L>>T>>O;
build(1,L,1);
while(O--){
scanf("%s",op);
if(op[0]=='C'){
scanf("%d%d%d",&a,&b,&c);
if(a>b) swap(a,b);
update(a,b,c,1,L,1);
}
else{
scanf("%d%d",&a,&b);
if(a>b) swap(a,b);
printf("%d\n",count(query(a,b,1,L,1)));
}
}
return 0;
}