HOJ2688 Color Segment

Color Segment

lilu又开始画画了,爸爸给他买了很多很多的画笔,他很高兴,于是就开始在一条带子上乱画,带子长度为L,是用L个单位长度的带子连在一起形成的,标号从1开始。由于过于兴奋,所以只是机械的做一个动作,就是将连续的一些带子涂上一种颜色。

爸爸看见了,感觉他在浪费画笔,于是问了一些问题,如果他回答正确就可以继续画,否则将被没收画笔。

问题是要询问一段连续的带子上有多少个色带(两个相邻标号的的带子并且具有同样的颜色,那么属于同一个色带)

lilu被吓怕了,所以不会想问题了,所以他找你,希望你能帮助他。

注:带子开始时的颜色默认为1颜色

Input

多组case,每组case的第一行输入三个整数L(1 ≤ L ≤ 200000),Color(1 ≤ C ≤ 1000),Q(1 ≤ Q ≤ 100000).Color表示画笔的种类(颜色从1开始编号),Q表示有Q个动作。接下来Q行输入,有两种输入:

1 A B C表示lilu画画的动作(1 ≤ A,B ≤ L , 1 ≤ C ≤ Color),表示从标号为A的带子到标号为B的带子之间都涂上C颜色。

0 A B表示爸爸询问的动作(1 ≤ A,B ≤ L),表示询问从下标为A的带子到下标为B的带子之间的色带个数。

注:B不一定小于A

Output

每组case,每个case中每个询问对应一行输出,输出一个整数表示色带的个数。

Sample Input/H3>

5 5 5
1 1 2 2
1 2 3 4
0 1 4
1 2 2 1
0 1 4

Sample Output

3
4

修改区间颜色,查询区间颜色总数没有参照常规的线段树写法,现修改现建树即:
  • 起初只有一条线段,颜色为1
  • 之后若添加了某条线段,则在之前的线段里查询,有的话就修改,没有的话建立,这样可以提速很多
  • 外加一些小优化:
    1. 某条线段包含在要修改的区间内,直接修改其颜色,并删除掉子树
    2. 某条线段单色,且颜色与要修改的颜色相同,直接返回
    3. 某条线段单色,删除子树
    4. 查询时,某条线段单色,直接返回

    #include <iostream>
    #include <cstdio>
    #define N 1500000
    using namespace std;
    
    struct CNode
    {
        int m_nLow;         //区间下界
        int m_nHigh;        //区间上届
        int m_nTot;         //颜色总数
        int m_nLeft;        //左儿子
        int m_nRight;       //右儿子
        int m_nLeftColor;   //最左边的颜色
        int m_nRightColor;  //最右边的颜色
    } p[N];
    
    //只建立一个区间,统一成col颜色
    void build(int a,int b,int root,int col)
    {
        p[root].m_nLow = a;
        p[root].m_nHigh = b;
        p[root].m_nTot = 1;
        p[root].m_nLeftColor = p[root].m_nRightColor = col;
        p[root].m_nRight = p[root].m_nLeft = 0;
    }
    
    //着色
    void paint(int a,int b,int root,int col)
    {
        //[a,b]不在root范围内,pass
        if(b<p[root].m_nLow || a>p[root].m_nHigh)return;
        //[a,b]包含root范围,直接将root绘成一种颜色,并删除掉子树
        if(a<=p[root].m_nLow && b>=p[root].m_nHigh)
        {
            p[root].m_nLeft = p[root].m_nRight = 0;  //删除掉子树
            p[root].m_nLeftColor = p[root].m_nRightColor = col; //将左右颜色赋值
            p[root].m_nTot = 1; //总数变为1
        }
        else
        {
            //[a,b]与root范围有交集
            //root有左右子树
            if(p[root].m_nLeft)
            {
                //绘制左子树
                paint(a,b,p[root].m_nLeft,col);
                //绘制右子树
                paint(a,b,p[root].m_nRight,col);
                //算总数
                p[root].m_nTot=p[p[root].m_nLeft].m_nTot + p[p[root].m_nRight].m_nTot -
                               (p[p[root].m_nLeft].m_nRightColor==p[p[root].m_nRight].m_nLeftColor);
                //维护左右端点的颜色
                p[root].m_nRightColor = p[p[root].m_nRight].m_nRightColor;
                p[root].m_nLeftColor = p[p[root].m_nLeft].m_nLeftColor;
                //如果单色的话,删掉子树
                if(p[root].m_nTot==1)
                {
                    p[root].m_nRight = p[root].m_nLeft = 0;
                }
            }
            else
            {
                //左右子树不存在,说明原先是单色
                //如果要绘制的颜色与原来颜色相同,则不用绘制
                if(p[root].m_nLeftColor==col)return;
                //否则建立子树
                p[root].m_nRight = root*2+1;
                p[root].m_nLeft = root*2;
    
                if(p[root].m_nLow < a && p[root].m_nHigh > b )
                {
                    //[a,b]完全包含在root范围里,并且端点也不在root端点上
                    //以b为分界点建立root的两个子树,并将颜色赋值为root颜色的值,则[a,b]包含在左子树里
                    build(p[root].m_nLow,b,root*2,p[root].m_nRightColor);
                    build(b+1,p[root].m_nHigh,root*2+1,p[root].m_nRightColor);
                    //将左子树里的[a,b]区间着色
                    paint(a,b,root*2,col);
                }
                else if(p[root].m_nLow >= a)
                {
                    //[a,b]在左边跟root范围相交
                    //同样以b为分界点建立两子树,并且直接将左子树颜色置为col,右子树为原root颜色
                    build(p[root].m_nLow,b,root*2,col);
                    build(b+1,p[root].m_nHigh,root*2+1,p[root].m_nRightColor);
                    //修改root左端点颜色值
                    p[root].m_nLeftColor = col;
                }
                else
                {
                    //[a,b]在右边跟root范围相交
                    //以a为分界点建立两子树,右子树颜色直接置为col,左子树为原root颜色
                    build(p[root].m_nLow,a - 1,root*2,p[root].m_nLeftColor);
                    build(a,p[root].m_nHigh,root*2+1,col);
                    //修改root右端点颜色值
                    p[root].m_nRightColor = col;
                }
                //维护root颜色数量
                p[root].m_nTot=p[p[root].m_nLeft].m_nTot + p[p[root].m_nRight].m_nTot -
                               (p[p[root].m_nLeft].m_nRightColor==p[p[root].m_nRight].m_nLeftColor);
            }
        }
    }
    
    int ask(int a,int b,int root)
    {
        if(p[root].m_nTot>1)
        {
            //如果root颜色数量大于1则需要向下查询
            if(a<=p[root].m_nLow && b>=p[root].m_nHigh)return p[root].m_nTot;
            if(b<p[p[root].m_nRight].m_nLow)return ask(a,b,root*2);
            if(a>p[p[root].m_nLeft].m_nHigh)return ask(a,b,root*2+1);
            return ask(a,b,root*2)+ask(a,b,root*2+1)-(p[p[root].m_nLeft].m_nRightColor==p[p[root].m_nRight].m_nLeftColor);
        }
        else
        {
            //root颜色数量为1,不必向下查询
            return 1;
        }
    }
    
    int main()
    {
        int L,Color,Question,flag,a,b,c;
        while(scanf("%d %d %d",&L,&Color,&Question)==3)
        {
            build(1,L,1,1);
            while(Question--)
            {
                scanf("%d",&flag);
                if(flag)
                {
                    scanf("%d %d %d",&a,&b,&c);
                    //另a<b
                    if(a>b)
                    a^=b^=a^=b;
                    paint(a,b,1,c);
                }
                else
                {
                    scanf("%d %d",&a,&b);
                    if(a>b)
                    a^=b^=a^=b;
                    printf("%d\n",ask(a,b,1));
                }
            }
        }
        return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值