学习了几天线段树,做了几道线段树题,挺有成就感的\(^o^)/~
POJ2528 Mayor's posters
线段树入门题。题目抽像出来就是:给你一根长为L(L = 10000000)的数轴,然后给出N(N <= 10000)组区间[Li, Ri]将数轴对应部分染颜色i,后面染的颜色的会把前面的给覆盖掉。最后要求出数轴上总共有多少种颜色。由于数轴很长,直接建线段树肯定会超时,而N很小,所以考虑离散化。比如给出的区间是:[1, 4]、[4, 1000000],我们把端点去重后排序:1、4、1000000,然后给定离散化值:1、2、3。然后区间就变成[1, 2]、[2, 3]了,变小了,所以肯定就不会超时啦!但Discuss里有大牛说这样离散化是有问题的,比如给出几个区间:[1, 10]、[1, 2]、[7, 10],去重排序:1、2、7、10,离散化:1、2、3、4,则区间变为[1, 4]、[1, 2]、[3, 4]。看,问题来了吧?这样做我们的结果是2(种不同颜色),而实际结果是3(种不同颜色)。原因就是3~6这一段被我们消掉了!注意一下排序过程:1、2、7、10,显然2和7之间的东西会没的。所以我们重新用一种离散化方式,当排序后相邻点距离大于1,则离散量+2,否则+1。于是离散化后变成:1、2、4、6,即区间变为[1, 6]、[1, 2]、[4, 6],得到的结果仍然是3。
然后不知道是哪位大牛的解题报告说要把区间倒着插入,这样有一个好处是不需要考虑覆盖问题。但我超时了好多次啊!后来参考他的牛码,发现他竟然也是正着插入的,我狂晕!然后我改为正着插入就很快AC了,高兴啊!
POJ2828 Buy Tickets
这道题还是蛮有意思的(恩,题目很有意思),讲的是插队的过程。售票员的位置是0,然后有N个人,每个人都插队,第i个人可以插他前面0~i-1个人的队,即跑到那个人后面。要求出最终队列。解释一下样例:
4 //4个人 0 77 //第1个人(他/她的编号是77)插第0个人的位置 1 51 //第2个人(他/她的编号是51)插第1个人的位置 1 33 //第3个人(他/她的编号是33)插第1个人的位置 2 69 //第4个人(他/她的编号是69)插第2个人的位置
有图有真相哦(*^__^*) 嘻嘻……
最终队列就是77 33 69 51。
做法是倒着插,因为最后一个人的位置一定是确定的,所以我们可以确定最后一个人的位置(木有错吧?)。然后,剩下的人也以同样的方式,插第k个人的位置表示前面有k个空格。比如上面的例子,先确定最后一个人的位置:[空, 空, 69, 空]。然后剩下三个人的位置肯定在空的位置处(并且他们是连在一起的,因为队伍肯定是连续的嘛),我们在插第三个人(1, 33)的时候,可以想像前两个人已经存在了,但不知道他们的位置在哪里,不管,反正它们是连续的(肯定是第一和第二个空),我只要前面有1个人(空格),然后插入即可(此时第二个人会被移到第四个格,不管它,想像而已,嘻嘻),然后变成[空, 33, 69, 空]。然后就照这样下去插就完成了。线段树的结点存放这一段里面有多少个空格,然后就可以很快地确定要插入的位置了,好棒啊!
POJ2777 Count Color
同第一题类似,抽象出来就是:给一根长度为L(L <= 100000)的数轴,一开始数轴上都是颜色1。然后给出O个操作,两种形式,一种是C L R COL,表示把数轴[L, R]这一段染成颜色COL(COL <= 30);另一种形式是P L R,表示询问[L, R]这一段上有多少种不同的颜色。
这题比较好的地方就是不用离散化,然后由于颜色数很少,可以用位来表示。结点存放颜色信息,比如这一段上有颜色1, 4, 7,就用一个32位数表示001001001。而两段上面的颜色就是这个信息的一个或。
POJ2750 Potted Flower
变态题啊!给一个有N个结点的环,每个结点上各一个可正可负可零的数值。现要取连续k个结点(1 <= k <= N - 1)的一段,使这一段的和最大。并且会经常出现修改某个结点的值的情况发生的,每修改一次就要输出一个最大的和。
把环拆成链1, 2, ..., N,一开始以为,只要找出链中和最小的那一段,然后用总和减去那一段就是最大和,可后来发现错了。比如要取的这一段是在中间:ABBBBBA,那么这种假设就不成立了。想一想所有可能出现的情况:一种是该链中某段得到最大和,另一种是链的前一部分和后一部分连起来构成最大和。对于后一种情况,其实就造价于找出链的最小和,用总和去减。所以,我的线段树一定要存储该段内的连续最大和与连续最小和,然后取这两种情况的最大值即是题目要求。为了得到最大和与最小和,结点存放的信息有:该段区间总和Sum、区间最大和VMax,区间最小和VMin,区间左端点开始连续的一段的最大和值LMax与最小和值LMin, 区间右端点开始连续的一段的最大和值RMax与最小和值RMin。然后以最大值为例有:
Sum[I] = Sum[L] + Sum[R]
LMax[I] = max(LMax[L], Sum[L] + LMax[R])
RMax[I] = max(RMax[R], Sum[R] + RMax[L])
VMax[I] = max(VMax[L], VMax[R], RMax[L] + LMax[R])
然后,由于不能取走N个点,需要加个判断:如果全总为正数(因为只有这种情况才会取走N个点),就用总和减去最小段和。否则,就取max(VMax, Sum - VMin)。