经典的线段树题目。具体解法是成段更新,在利用一个vis[]数组在查询一次整段区间时,记录没有被覆盖到的颜色,并对其所对应的编号标记为1.而在成段更新结点时,若某区间将出现混合色,就让其子节点更新为它的颜色,并标记该结点。总之,对于成段更新区间属性的题目一般用线段树来解决,使用时要保存结点属性的单一性,但更新破坏了这种单一性时,则应递归地保持该结点孩子的属性的单一性。只有这样,在更新操作才不至于退化到O(n)的复杂度,保证高效率。
以下是代码:
- #include<cstdio>
#include<iostream>
using namespace std; - const int M=100100;
struct Tree
{
int col;
int l,r;
}T[3*M];
bool vis[M];
int l,t,o; - void build(int l,int r,int p)
{
T[p].l=l;
T[p].r=r;
if(l==r)
{
T[p].col=1;
return ;
}
int mid=(l+r)>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
} - void update(int l,int r,int x,int p)
{
if(T[p].l==l && T[p].r==r)
{
T[p].col=x;
}
else
{
if(T[p].col>0)
{
T[p<<1].col=T[p<<1|1].col=T[p].col;
T[p].col=-1;
}
int mid=(T[p].l+T[p].r)>>1;
if(r<=mid)
{
update(l,r,x,p<<1);
}
else if(l>mid)
{
update(l,r,x,p<<1|1);
}
else
{
update(l,mid,x,p<<1);
update(mid+1,r,x,p<<1|1);
}
}
} - void cal(int l,int r,int p)
{
if(T[p].col>0)
{
vis[T[p].col]=1;
}
else
{
int mid=(T[p].l+T[p].r)>>1;
if(r<=mid) cal(l,r,p<<1);
else if(l>mid) cal(l,r,p<<1|1);
else
{
cal(l,mid,p<<1);
cal(mid+1,r,p<<1|1);
}
}
} - int main()
{
// freopen("in.txt","r",stdin);
scanf("%d%d%d",&l,&t,&o);
char str[10];
int a,b,c,temp,ans;
int i;
build(1,l,1);
T[1].col=1;
while(o--)
{
scanf("%s",str);
if(str[0]=='C')
{
scanf("%d%d%d",&a,&b,&c);
if(a>b)
{
temp=a;a=b;b=temp;
}
update(a,b,c,1);
}
else
{
scanf("%d%d",&a,&b);
if(a>b)
{
temp=a;a=b;b=temp;
}
memset(vis,0,sizeof(vis));
cal(a,b,1);
ans=0;
for(i=1;i<=t;i++)
{
if(vis[i]) ans++;
}
printf("%d/n",ans);
}
}
return 0;
}