题目链接:2777 -- Count Color
翻译:有一个很长的板子,长L厘米,L是一个正整数,所以我们可以把板子平均分成L段,从左到右用1,2,...L标记,每段1厘米长。现在我们必须给棋盘上色——只有一种颜色的一段。我们可以在棋盘上做以下两种操作:
1. "CAB C" 将棋盘从 A 段到 B段涂上颜色 C。
2. "PA B" 输出 A 段和 B 段之间绘制的不同颜色的数量。
分析:我们可以把一个区间存在的颜色用一个数的二进制来表示,其实这个很简单,比如某一段区间的颜色是9,其对应的二进制为1001,这就意味着这段区间上存在2种颜色,也就是说某段区间的颜色种数就是这段区间颜色值的二进制表示中1的个数,至于pushup操作,就是当前区间的两个子区间的或值,然后剩下的就是一个线段树模板了。下面直接上代码了:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=1e6+10;
int sum[N],l[N],r[N],lazy[N];
//返回x中1的个数
int lowbit(int x)
{
int cnt=0;
while(x)
{
cnt++;
x-=x&-x;
}
return cnt;
}
//用二进制表示颜色,用或求区间颜色总数
void pushup(int id)
{
sum[id]=sum[id<<1]|sum[id<<1|1];
}
void pushdown(int id)
{
if(lazy[id])
{
lazy[id<<1]=lazy[id];
lazy[id<<1|1]=lazy[id];
sum[id<<1]=lazy[id];
sum[id<<1|1]=lazy[id];
lazy[id]=0;
}
}
void build(int id,int L,int R)
{
l[id]=L;r[id]=R;sum[id]=1;lazy[id]=0;
if(L==R) return ;
int mid=L+R>>1;
build(id<<1,L,mid);
build(id<<1|1,mid+1,R);
pushup(id);
}
void update_interval(int id,int L,int R,int val)
{
if(l[id]>R||r[id]<L) return ;
if(l[id]>=L&&r[id]<=R)
{
sum[id]=val;
lazy[id]=val;
return ;
}
pushdown(id);
update_interval(id<<1,L,R,val);
update_interval(id<<1|1,L,R,val);
pushup(id);
}
int query_interval(int id,int L,int R)
{
if(l[id]>R||r[id]<L) return 0;
if(l[id]>=L&&r[id]<=R) return sum[id];
pushdown(id);
return query_interval(id<<1,L,R)|query_interval(id<<1|1,L,R);
}
int main()
{
int n,t,q;
cin>>n>>t>>q;
build(1,1,n);
char op[20];
int x,y,z;
while(q--)
{
scanf("%s",op);
//x和y相对大小不确定,坑死我了
if(op[0]=='C')
{
scanf("%d%d%d",&x,&y,&z);
update_interval(1,min(x,y),max(x,y),1<<z-1);
}
else
{
scanf("%d%d",&x,&y);
printf("%d\n",lowbit(query_interval(1,min(x,y),max(x,y))));
}
}
return 0;
}