【POJ2777】Count Color-线段树区间更新

来源:互联网 发布:淘宝16年售假处罚降权 编辑:程序博客网 时间:2024/05/21 06:19

(本人本题完成于2016-7-21)

题目大意:有一条长度为N的线段,被分成长度为1的N段,标号为1,2,...,N。现在要给这条线段上色,一共有T(不超过30)种颜色,标号为1,2,...,T,刚开始所有段的颜色都是1。有两种操作:1.C a b c:将区间(a,b)内的段的颜色改成c;2.P a b:询问区间(a,b)内的段有多少种不同的颜色。a可能大于b。你要做的就是根据询问给出答案。

做法:建一棵线段树,除区间左右端点外再维护一个值n,如果n不为0,则表示该区间内的段的颜色均为n,如果n为0,则表示该区间内的段不只有一种颜色。建树时将所有n初始化为1。查询时,如果所查询到的区间的n值不为0,则将其统计入标记数组v中,并停止向下扩展。

以下是本人代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;int n,t,o,a,b,c,ans;char x;bool v[35]={0}; //v数组统计出现过的颜色struct node{  int l,r;  int n;}seg[300010];void buildtree(int no,int l,int r){  int mid=(l+r)/2;  seg[no].l=l;seg[no].r=r;seg[no].n=1;  if (l!=r)  {    buildtree(2*no,l,mid);buildtree(2*no+1,mid+1,r);  }}void col(int no,int s,int t,int c){  int mid=(seg[no].l+seg[no].r)/2;  if (seg[no].l>=s&&seg[no].r<=t) seg[no].n=c; //lazy思想,n同时作为标记使用  else  {    if (seg[no].n!=0) seg[2*no].n=seg[2*no+1].n=seg[no].n; //将标记下放    if (s<=mid) col(2*no,s,t,c);if (t>mid) col(2*no+1,s,t,c);if (seg[2*no].n==seg[2*no+1].n) seg[no].n=seg[2*no].n;else seg[no].n=0; //更新节点  }}void query(int no,int s,int t){  int mid=(seg[no].l+seg[no].r)/2;  if (seg[no].n!=0) v[seg[no].n]=1; //若seg[no].n不为0,将该颜色统计入v数组  else  {    if (seg[no].n!=0) seg[2*no].n=seg[2*no+1].n=seg[no].n;if (s<=mid) query(2*no,s,t);if (t>mid) query(2*no+1,s,t);if (seg[2*no].n==seg[2*no+1].n) seg[no].n=seg[2*no].n;else seg[no].n=0;  }}int main(){  while(scanf("%d %d %d\n",&n,&t,&o)!=EOF)  {    buildtree(1,1,n);    for(int i=1;i<=o;i++)    {  scanf("%c",&x);  if (x=='C')  {    scanf("%d %d %d\n",&a,&b,&c);if (a>b) {int t=a;a=b;b=t;} //a可能大于b,特殊处理col(1,a,b,c); //上色:区间更新  }  if (x=='P')  {    scanf("%d %d\n",&a,&b);if (a>b) {int t=a;a=b;b=t;} //a可能大于b,特殊处理                memset(v,0,sizeof(v));
                query(1,a,b); //查询
                ans=0;
                for(int j=1;j<=t;j++) if (v[j]) ans++; //统计出现过的不同颜色数目    
                printf("%d\n",ans);
          }
    }
  }
  return 0;
}

0 0