poj 2777 Count Color

线段树

题意:输出N,T,M,表示总区间长度([1,N]),有T种颜色(1到T表示),接下来有M个操作。C,a,b,c类型的操作是成段更新,把区间[a,b]的颜色改为c。P,a,b类型的操作是询问区间[a,b]有多少种不同的颜色

分析都在代码里面了

/*
成段更新,询问一个区间内有多少种的不同的颜色
每次询问之前清空一个颜色的标记数组,然后开始询问,去到一个区间,若val为1说明整段的颜色相同,
不用再继续深入,若为0说明颜色不止一种要继续深入。每次val为1时要判断这个颜色col是否已经被记录,
只有没被记录的才能计数,而且要记录该颜色已经被用过
至于成段更新,要用到LAZY。到达目标区间后不要再继续深入。而从根节点往下走的过程中要不断把父节点的val值传递给两个孩子
*/

#include <cstdio>
#include <cstring>
#define MAX 100010
#define    MAXT 35
#define get_mid(a,b) (a+b)>>1
#define lchild(a) a<<1
#define rchild(a) a<<1|1

struct node
{
    int l,r,val,col;
}t[4*MAX];
int N,M,T,used[MAXT];

void build(int a ,int b ,int rt)
{
    t[rt].l=a; t[rt].r=b; t[rt].val=1; t[rt].col=1;
    if(a==b) return ;
    int mid=get_mid(a,b);
    build(a,mid,lchild(rt));
    build(mid+1,b,rchild(rt));
}

void updata(int a ,int b ,int col , int rt)
{
    if(t[rt].val && t[rt].col==col) return ;
    //小小的剪枝,若当前区间是单色的而且颜色与要更新的区间颜色相同那么没必要更新,直接返回
    if(t[rt].l==a && t[rt].r==b) //目标区间
    {
        t[rt].col=col;
        t[rt].val=1;
        return ;
    }
    int mid=get_mid(t[rt].l,t[rt].r);
    if(t[rt].val)
    {//单前区间是单色的,但是还要继续深入,将不再是单色,要及时将父节点的val和col信息传递给左右孩子
        t[lchild(rt)].val=1; t[lchild(rt)].col=t[rt].col;
        t[rchild(rt)].val=1; t[rchild(rt)].col=t[rt].col;
        t[rt].val=0;
    }
    if(a>mid) //只访问右孩子
        updata(a,b,col,rchild(rt));
    else if(b<=mid) //只访问左海子
        updata(a,b,col,lchild(rt));
    else //左右孩子均访问
    {
        updata(a,mid,col,lchild(rt));
        updata(mid+1,b,col,rchild(rt));
    }
}

int query(int a ,int b ,int rt)
{
    if(t[rt].val)
    {//当前区间单色那么没必要再往下走
        if(!used[t[rt].col])
        {
            used[t[rt].col]=1;
            return 1;
        }
        else return 0;
    }
    int mid=get_mid(t[rt].l,t[rt].r);
    if(a>mid) //只访问右孩子
        return query(a,b,rchild(rt));
    else if(b<=mid) //只访问左孩子
        return query(a,b,lchild(rt));
    else //左右均访问
        return query(a,mid,lchild(rt))+query(mid+1,b,rchild(rt));
}

int main()
{
    while(scanf("%d%d%d",&N,&T,&M)!=EOF)
    {
        build(1,N,1);
        for(int i=1; i<=M; i++)
        {
            char s[5]; int a,b,col;
            scanf("%s",s);
            if(s[0]=='C') //成段更新
            {
                scanf("%d%d%d",&a,&b,&col);
                updata(a,b,col,1);
            }
            else //询问
            {
                scanf("%d%d",&a,&b);
                memset(used,0,sizeof(used));
                printf("%d\n",query(a,b,1));
            }
        }
    }
    return 0;
}

 

 

转载于:https://www.cnblogs.com/scau20110726/archive/2013/02/23/2923253.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值