HOJ2688 Color Segment

来源:互联网 发布:微信网页授权域名 编辑:程序博客网 时间:2024/06/05 10:53

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 51 1 2 21 2 3 40 1 41 2 2 10 1 4

Sample Output

34

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

    #include <iostream>#include <cstdio>#define N 1500000using 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;}