hdu5023 线段树

题目链接: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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值