(本人本题完成于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,&t,&o)!=EOF)
{
buildtree(1,1,n);
for(int i=1;i<=o;i++)
{
scanf("%c",&x);
if (x=='C')
{
scanf("%d %d %d
",&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
",&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
",ans);
}
}
}
return 0;
}