poj 2777 Count Color
线段树
题意:输出N,T,M,表示总区间长度([1,N]),有T种颜色(1到T表示),接下来有M个操作。C,a,b,c类型的操作是成段更新,把区间[a,b]的颜色改为c。P,a,b类型的操作是询问区间[a,b]有多少种不同的颜色
分析都在代码里面了
/* 成段更新,询问一个区间内有多少种的不同的颜色 每次询问之前清空一个颜色的标记数组,然后开始询问,去到一个区间,若val为1说明整段的颜色相同, 不用再继续深入,若为0说明颜色不止一种要继续深入。每次val为1时要判断这个颜色col是否已经被记录, 只有没被记录的才能计数,而且要记录该颜色已经被用过 至于成段更新,要用到LAZY。到达目标区间后不要再继续深入。而从根节点往下走的过程中要不断把父节点的val值传递给两个孩子 */ #include <cstdio> #include <cstring> #define MAX 100010 #define MAXT 35 #define get_mid(a,b) (a+b)>>1 #define lchild(a) a<<1 #define rchild(a) a<<1|1 struct node { int l,r,val,col; }t[4*MAX]; int N,M,T,used[MAXT]; void build(int a ,int b ,int rt) { t[rt].l=a; t[rt].r=b; t[rt].val=1; t[rt].col=1; if(a==b) return ; int mid=get_mid(a,b); build(a,mid,lchild(rt)); build(mid+1,b,rchild(rt)); } void updata(int a ,int b ,int col , int rt) { if(t[rt].val && t[rt].col==col) return ; //小小的剪枝,若当前区间是单色的而且颜色与要更新的区间颜色相同那么没必要更新,直接返回 if(t[rt].l==a && t[rt].r==b) //目标区间 { t[rt].col=col; t[rt].val=1; return ; } int mid=get_mid(t[rt].l,t[rt].r); if(t[rt].val) {//单前区间是单色的,但是还要继续深入,将不再是单色,要及时将父节点的val和col信息传递给左右孩子 t[lchild(rt)].val=1; t[lchild(rt)].col=t[rt].col; t[rchild(rt)].val=1; t[rchild(rt)].col=t[rt].col; t[rt].val=0; } if(a>mid) //只访问右孩子 updata(a,b,col,rchild(rt)); else if(b<=mid) //只访问左海子 updata(a,b,col,lchild(rt)); else //左右孩子均访问 { updata(a,mid,col,lchild(rt)); updata(mid+1,b,col,rchild(rt)); } } int query(int a ,int b ,int rt) { if(t[rt].val) {//当前区间单色那么没必要再往下走 if(!used[t[rt].col]) { used[t[rt].col]=1; return 1; } else return 0; } int mid=get_mid(t[rt].l,t[rt].r); if(a>mid) //只访问右孩子 return query(a,b,rchild(rt)); else if(b<=mid) //只访问左孩子 return query(a,b,lchild(rt)); else //左右均访问 return query(a,mid,lchild(rt))+query(mid+1,b,rchild(rt)); } int main() { while(scanf("%d%d%d",&N,&T,&M)!=EOF) { build(1,N,1); for(int i=1; i<=M; i++) { char s[5]; int a,b,col; scanf("%s",s); if(s[0]=='C') //成段更新 { scanf("%d%d%d",&a,&b,&col); updata(a,b,col,1); } else //询问 { scanf("%d%d",&a,&b); memset(used,0,sizeof(used)); printf("%d\n",query(a,b,1)); } } } return 0; }