题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5023
题意:有最多30种颜色,一个长度为n的墙。一开始所有墙的颜色都是2。
两种操作:
P l r x :把区间[l,r]中所有的墙的颜色都改为x
Q l r :询问[l,r]里面的颜色,把颜色升序输出
思路:普通的线段树区间更新已经区间询问。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5e4;
#define lson rt<<1
#define rson rt<<1|1
int n,m;
int tree[maxn<<4];//建议开大点,不然hdu会乱返回成超时,实际是数组太小
void pushup(int rt)
{
if(tree[lson]==tree[rson])tree[rt]=tree[lson];//如果两个儿子的颜色是一样的,说明大区间颜色一样
else tree[rt]=0;//否则,整个区间颜色不一,大区间改为0
}
void pushdown(int rt)
{
if(!tree[rt])return;//如果大区间的颜色都是不确定的,没有必要往下传
tree[lson]=tree[rson]=tree[rt];//大区间颜色确定,说明下面所有小区间颜色都是一样的
tree[rt]=0;//传好后重新置为0
}
void bulid(int rt,int l,int r)//建一开始的树,当然也可以直接通过updata来获得树
{
if(l==r)
{
tree[rt]=2;//一开始的颜色是2
return;
}
int mid=(l+r)>>1;
bulid(lson,l,mid);
bulid(rson,mid+1,r);
pushup(rt);
}
void updata(int rt,int l,int r,int x,int y,int v)
{
if(l>=x&&r<=y)
{
tree[rt]=v;//找到大区间后,把大区间的颜色更改,下面的小区间等要用到的时候更新
return;
}
int mid=(l+r)>>1;
pushdown(rt);//更新小区间颜色
if(y<=mid)updata(lson,l,mid,x,y,v);
else if(x>=mid+1)updata(rson,mid+1,r,x,y,v);
else updata(lson,l,mid,x,mid,v),updata(rson,mid+1,r,mid+1,y,v);
pushup(rt);//向上更新父亲
}
int ans[35];
void query(int rt,int l,int r,int x,int y)
{
if(tree[rt])//如果这个大区间的颜色是知道的,那么下面所有的小区间的颜色都一样,不用再找了
{
ans[tree[rt]]=1;
return;
}//大区间颜色不确定,再往下找
int mid=(l+r)>>1;
pushdown(rt);
if(y<=mid)query(lson,l,mid,x,y);
else if(x>=mid+1)query(rson,mid+1,r,x,y);
else query(lson,l,mid,x,mid),query(rson,mid+1,r,mid+1,y);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(!n&&!m)break;
bulid(1,1,n);
char o[2];
int x,y,z;
for(int i=1;i<=m;i++)
{
scanf("%s",o);
if(o[0]=='P')
{
scanf("%d%d%d",&x,&y,&z);
updata(1,1,n,x,y,z);
}
else if(o[0]=='Q')
{
scanf("%d%d",&x,&y);
fill(ans+1,ans+31,0);//预置为0
query(1,1,n,x,y);
bool fir=false;//注意行尾空格
for(int i=1;i<=30;i++)
{
if(ans[i])
{
if(!fir)printf("%d",i),fir=true;
else printf(" %d",i);
}
}
printf("\n");
}
}
}
return 0;
}
看了一些别人的博客,突然发现这题还有更妙的方法。因为颜色种数最大只有30,可以直接用二进制表示是否含有某种颜色。比如101表示有第1种和第3种颜色。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5e4;
#define lson rt<<1
#define rson rt<<1|1
int n,m;
int tree[maxn<<4],lazy[maxn<<4];//tree表示某个区间的颜色情况,lazy是懒惰标记
void pushup(int rt)
{
tree[rt]=tree[lson]|tree[rson];//大区间的颜色种数是两个子区间的并
}
void pushdown(int rt)
{
if(!lazy[rt])return;//懒惰标记是0的话,说明没有信息要往下传
lazy[lson]=lazy[rson]=lazy[rt];
tree[lson]=tree[rson]=lazy[rt];
lazy[rt]=0;
}
void bulid(int rt,int l,int r)
{
lazy[rt]=0;
if(l==r)
{
tree[rt]=2;//一开始的颜色是2
return;
}
int mid=(l+r)>>1;
bulid(lson,l,mid);
bulid(rson,mid+1,r);
pushup(rt);
}
void updata(int rt,int l,int r,int x,int y,int v)
{
if(l>=x&&r<=y)
{
lazy[rt]=1<<(v-1);//第v种颜色对应二进制位是1<<(v-1)
tree[rt]=1<<(v-1);
return;
}
int mid=(l+r)>>1;
pushdown(rt);
if(y<=mid)updata(lson,l,mid,x,y,v);
else if(x>=mid+1)updata(rson,mid+1,r,x,y,v);
else updata(lson,l,mid,x,mid,v),updata(rson,mid+1,r,mid+1,y,v);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
{
return tree[rt];
}
int mid=(l+r)>>1;
pushdown(rt);
int ret=0;//查询的时候是把所有符合条件的区间并起来,就是整个询问区间的总信息
if(y<=mid)ret|=query(lson,l,mid,x,y);
else if(x>=mid+1)ret|=query(rson,mid+1,r,x,y);
else ret|=query(lson,l,mid,x,mid),ret|=query(rson,mid+1,r,mid+1,y);
return ret;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(!n&&!m)break;
bulid(1,1,n);
char o[2];
int x,y,z;
for(int i=1;i<=m;i++)
{
scanf("%s",o);
if(o[0]=='P')
{
scanf("%d%d%d",&x,&y,&z);
updata(1,1,n,x,y,z);
}
else if(o[0]=='Q')
{
scanf("%d%d",&x,&y);
int ans=query(1,1,n,x,y);
int tp=0;
bool fir=false;
while(ans)
{
tp++;
if(ans&1)
{
if(!fir)printf("%d",tp),fir=true;
else printf(" %d",tp);
}
ans>>=1;
}
printf("\n");
}
}
}
return 0;
}