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
- 之后若添加了某条线段,则在之前的线段里查询,有的话就修改,没有的话建立,这样可以提速很多
- 外加一些小优化:
- 某条线段包含在要修改的区间内,直接修改其颜色,并删除掉子树
- 某条线段单色,且颜色与要修改的颜色相同,直接返回
- 某条线段单色,删除子树
- 查询时,某条线段单色,直接返回
#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; }