这是一个统计问题,大意是给定两种操作,C A B color:将区间[A,B]涂上颜色color,P A B统计并打印区间[A,B]上颜色的数目,可图颜色的种类不超过30种,区间的长度最大可达100000,给出的操作次数最多可达100000.
这是一个经典的线段树题目,由于颜色的数目最多只有30种,所以用32位整形的每个位表示一种对应的颜色是可行的,这样便可以很方便做集合并的操作。
#include<cstdio>
#include<cstring>
#include<iostream>
#define N 100100
using namespace std;
int l,t,o;
long long int color[N<<2];
int count(int k)//计算一个整数有多少个位为1
{
int ans=0;
while(k>0)
{
ans++;
k-=(k&(-k));//右边的表达式的作用是将K的最后一个1独立出来
//这借用了树状数组里使用的位运算方法
}
return ans;
}
void update(int rt,int ld,int rd,int s,int e,long long int c)
{
if(s<=ld && rd<=e) {
color[rt]=c;
return ;
}
int mid=(ld+rd)>>1;
int cnt = count(color[rt]);
if(cnt==1) {color[rt<<1]=color[rt];color[rt<<1|1]=color[rt];}
if(e<=mid) update(rt<<1,ld,mid,s,e,c);
else if(s>mid) update(rt<<1|1,mid+1,rd,s,e,c);
else {
update(rt<<1,ld,mid,s,mid,c);
update(rt<<1|1,mid+1,rd,mid+1,e,c);
}
color[rt]=color[rt<<1]|color[rt<<1|1];
}
int query(int rt,int ld,int rd,int s,int e)
{
if(s<=ld && rd<=e) return color[rt];
int mid=(ld+rd)>>1;
int cnt=count(color[rt]);
if(cnt==1) return color[rt];
if(e<=mid) return query(rt<<1,ld,mid,s,e);
else if(s>mid) return query(rt<<1|1,mid+1,rd,s,e);
else {
int c1=query(rt<<1,ld,mid,s,mid);
int c2=query(rt<<1|1,mid+1,rd,mid+1,e);
return c1|c2;
}
}
void getline(char *s)
{
char ch;
int i=0;
while(true) {
scanf("%c",&ch);
s[i++]=ch;
if(ch=='\n') break;
}
s[i]='\0';
}
int main()
{
while(scanf("%d%d%d",&l,&t,&o)!=EOF){
char str[20],ch;
for(int i=0;i<4*N;i++) color[i]=1;
scanf("%c",&ch);
for(int i=0; i<o; i++) {
getline(str);
int a,b,c;
sscanf(str,"%c%d%d",&ch,&a,&b);
int temp = a;
if(a>b) {
a=b;
b=temp;
}
if (ch=='C') {
sscanf(str,"%c%d%d%d",&ch,&a,&b,&c);
update(1,1,l,a,b,1<<(c-1));
}
if(ch=='P') printf("%d\n", count(query(1,1,l,a,b)));
}
}
return 0;
}