http://poj.org/problem?id=2777
以上是Poj2777原题链接
题目大意:
就是说给你一段区间,然后对它进行两种操作。首先可以将某一段区间涂为另外一种颜色。第二个操作就是查询一个区间内有多少不同的颜色。
解题思路:
首先暴力超时,因此用线段树。线段树的每个节点表示当前区间的不同的颜色个数。因为是区间,所以还是要用到延时更新。而且可以利用二进制和位运算巧妙的进行 颜色的运算,具体可以看代码,因为上道线段树区间更新题目理解不清楚。导致这次题目的代码刚开始写的丑陋无比。总之就是很差,知识不扎实,以后多加努力。以下贴代码及解析,需巧妙理解位运算的用法
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
int l,co,o,ans;
struct node
{
int l,r;
int mid;
int color;
}t[500000];
int a[50]; //记录颜色的数组
int mark[500000];
void pushup(int rt)
{
t[rt].color=t[rt<<1].color|t[rt<<1|1].color; //正常维护,从子节点更新父节点
}
void pushdown(int rt,int color) //延迟更新,从父节点更新子节点
{
if(mark[rt]!=-1)
{
mark[rt<<1]=mark[rt<<1|1]=mark[rt];
t[rt<<1|1].color=t[rt<<1].color=t[rt].color;
mark[rt]=-1;
}
}
void build(int l,int r,int rt) //建树
{
t[rt].l=l;
t[rt].r=r;
t[rt].mid=(l+r)/2;
t[rt].color=a[1];
if(t[rt].l==t[rt].r)
return ;
int mid=(l+r)/2;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
}
void query(int l,int r,int rt) //延迟更新,根据查询的节点进行更新操作
{
if(t[rt].l>=l&&t[rt].r<=r)
{
ans=ans|t[rt].color;
return ;
}
pushdown(rt,t[rt].color);
if(l<=t[rt].mid)
query(l,r,rt<<1);
if(r>t[rt].mid)
query(l,r,rt<<1|1);
}
void update(int l,int r,int color,int flag,int rt) //更新操作,需要思路清晰在写,打标记,延迟更新
{
if(l<=t[rt].l&&r>=t[rt].r)
{
mark[rt]=flag;
t[rt].color=color;
return ;
}
pushdown(rt,color);
if(l<=t[rt].mid)
update(l,r,color,1,rt<<1);
if(r>t[rt].mid)
update(l,r,color,1,rt<<1|1);
pushup(rt);
}
int main()
{
a[1]=1;
for(int i=2;i<=30;i++) //根据二进制储存颜色的种类
a[i]=a[i-1]*2;
while(scanf("%d%d%d",&l,&co,&o)!=EOF)
{
build(1,l,1);
memset(mark,-1,sizeof(mark));
for(int i=0;i<o;i++)
{
char k;
int p,q,r;
scanf(" %c",&k);
if(k=='C')
{
scanf("%d%d%d",&p,&q,&r);
update(p,q,a[r],1,1);
}
if(k=='P')
{
int p,q;
scanf("%d%d",&p,&q);
ans=0;
int t;
if(p>q) //神tm交换大小,题目刚开始没看清楚,wa了几发
{
t=p;
p=q;
q=t;
}
query(p,q,1);
int s=0;
for(int i=1;i<=30;i++)
{
if(ans&a[i])
s++;
}
printf("%d\n",s);
}
}
}
}