就先用一道题来做个引子吧:
你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作:
命令 |
参数限制 |
内容 |
1 x y A |
1<=x,y<=N,A是正整数 |
将格子x,y里的数字加上A |
2 x1 y1 x2 y2 |
1<=x1<= x2<=N 1<=y1<= y2<=N |
输出x1 y1 x2 y2这个矩形内的数字和 |
其中,n<=500000,m<=200000,空间限制20M
题目很恶心
难道要动用传说中的动态二维线段树?!真用个像树套树或是什么动态二维线段树之类的神级数据结构还是不太漂亮
怎么样才能漂亮地完成这种麻烦的统计呢?
说到漂亮,诸如归并排序,树状数组之类的东西就不免从脑中闪现
题解寥寥数语,使用归并的思想,先递归完成l~mid,mid+1~r,再用树状数组合并
What in the world is he saying?
。。。
重新分析一下问题,其实问题无非就是要求对于任意一个询问操作i,统计出满足j<i,x[j],y[j]在i要求的范围内的加上去的数的和
即问题要求统计出满足 输入顺序、横坐标、纵坐标 三个限制条件的点的权值之和
本来如果只有2个要求,就1维排序,另一维数据结构就可以了,但3维?
又想到归并的过程中,它是可以使得两边的数同时满足 原序和新序 的,换句话说,由于归并具有稳定性,在排序的过程中,它可以同时满足两个关键字都单调,即同时满足所要求的两个限制条件
那如果归并加上数据结构岂不能统计满足3个要求的信息了?
答案是肯定的
回到这道题,解法就出来了
先把询问一个矩形的那个操作分解为4个点事件,即x2,y2的+1事件、x2,y1-1和x1-1,y2的-1事件,还有x1-1,y1-1的+1事件(就是2维前缀和啦)
由于我们将询问递归的处理了,所以只要考虑合并的问题
而实际上只有处于左区间的插入操作对右区间里的询问操作所造成的影响我们没有在递归的时候统计到,所以合并的时候将左区间的插入统计到右区间的询问操作里就可以了
由于本身就要按读入的顺序作为一个关键字,所以直接以点事件的横坐标为关键字进行归并排序,在合并的同时用树状数组统计纵坐标这个关键字
总时间复杂度O(n(logn)^2),看起来跟二维线段树一样,但非常小的常数以及极低的编程复杂度都让这个算法完全胜过二维线段树
实现的时候有个非常实用的小技巧:树状数组每次使用前是要清零的,但如果每次都fillchar就显然TLE
怎么办?其实可以每次使用前就inc一下一个时间标记,树状数组的每个结点多开一个域,记它最后一次被使用时的时间标记是多少,要用这个结点时,看它的标记和现在的全局的时间标记是否相同,不相同则先把这个结点清零再该干嘛干嘛
附引题代码:
居然60行搞定,一遍ac!