這是一個統計問題,大意是給定兩種操作,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 100100using 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;}