就是给你一个1..L的线段,不停地染色,并询问区间上由多少种颜色组成。
由于颜色数很小(不大于30),便可以用一个32位整数来表示颜色的集合。
自底向上更新是做集合的并(就是按位或)。
同样只有在纯色时才需要下传标记。
敬请指教,神犇轻喷0v0
#include<cstdio>
#include<cstring>
#include<iostream>
#define mid (l+r>>1)
using namespace std;
typedef int arr[201001];
arr Lc,Rc,c;//c是颜色的集合
int tot,tree;
int cnt(int c){
c=(c&0x55555555)+((c>>1)&0x55555555);
c=(c&0x33333333)+((c>>2)&0x33333333);
c=(c&0x0f0f0f0f)+((c>>4)&0x0f0f0f0f);
c=(c&0x00ff00ff)+((c>>8)&0x00ff00ff);
c=(c&0x0000ffff)+((c>>16)&0x0000ffff);
return c;
}
void build(int &x,int l,int r){
x=++tot;
if(l+1<r){
build(Lc[x],l,mid);
build(Rc[x],mid,r);
c[x]=c[Lc[x]]|c[Rc[x]];
}
else c[x]|=1;
}
void paint(int x,int l,int r,int L,int R,int color){
if(L<=l&&r<=R)
c[x]=1<<(color-1);
else{
if((c[x]&-c[x])==c[x]) c[Lc[x]]=c[Rc[x]]=c[x];
if(L<mid) paint(Lc[x],l,mid,L,R,color);
if(R>mid) paint(Rc[x],mid,r,L,R,color);
c[x]=c[Lc[x]]|c[Rc[x]];
}
// printf("[%d,%d):%d\n",l,r,c[x]);
}
int query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R) return c[x];
// printf("[%d,%d):%d\n",l,r,c[x]);
if((c[x]&(-c[x]))==c[x]) c[Lc[x]]=c[Rc[x]]=c[x];
int ans=0;
if(L<mid) ans|=query(Lc[x],l,mid,L,R);
if(R>mid) ans|=query(Rc[x],mid,r,L,R);
return ans;
}
inline void read(int &x){
x=0;
char ch=getchar();
if(ch==-1)return;
while((ch<'0'||ch>'9')&&ch!=-1) ch=getchar();
do{x=x*10+ch-48;ch=getchar();}while(ch>='0'&&ch<='9');
}
int main(){
// cout<<cnt(0x7fffffff)<<"\n";
int l,t,o,x,y,z;
char ch;
read(l);read(t);read(o);
build(tree,1,l+1);
while(o--){
do{
ch=getchar();
}while(ch!='C'&&ch!='P');
read(x);read(y);
if(x>y) swap(x,y);
if(ch=='C'){
read(z);
paint(1,1,l+1,x,y+1,z);
}
else printf("%d\n",cnt(query(1,1,l+1,x,y+1)));
}
return 0;
}