// 668K 422MS G++ #include <stdio.h> #include <string.h> const int MAX = 32767; const int LEVEL_MAX = 15100; int tree[MAX<<1]; int levelNum[LEVEL_MAX]; int starNum; void update(int curId, int x, int begin, int end) { if (begin > end) { return; } tree[curId]++; if (begin == x && end == x) { // enter tree's bottom return; } int mid = (begin + end) >> 1; if (x <= mid) { // x in left part update(curId<<1, x, begin, mid); } else { // x in right part update(curId<<1|1, x, mid+1, end); } } int Query(int curId, int xbegin, int xend, int begin, int end) { // printf("%d %d %d %d\n", xbegin, xend, begin, end); if (xbegin == begin && xend == end) { return tree[curId]; } int mid = (begin + end) >> 1; if (mid >= xend) { // total in left part, no need check xbegin <= begin return Query(curId<<1, xbegin, xend, begin, mid); } else if (xbegin > mid) { // total in right part. return Query(curId<<1|1, xbegin, xend, mid+1, end); } else { return Query(curId<<1, xbegin, mid, begin, mid) + Query(curId<<1|1, mid+1, xend, mid+1, end); } } int main() { while(scanf("%d", &starNum) != EOF) { int x; int y; memset(tree, 0, sizeof(tree)); memset(levelNum, 0, sizeof(levelNum)); for (int i = 1; i <= starNum; i++) { scanf("%d %d", &x, &y); // int sum = 0; int sum = Query(1, 0, x, 0, MAX); update(1, x, 0, MAX); // printf("%d\n",sum); levelNum[sum]++; } for (int i = 0; i < starNum; i++) { printf("%d\n", levelNum[i]); } } }
线段树处女练手题,为了熟悉线段树的构建和使用,线段树其实属于计算几何的一部分,主要处理一维的覆盖和区间问题。
实现形式和堆基本一致,都是用一维数组模拟满二叉树, 左右孩子的形式也是一样的表现形式。
这道题还很贴心的说明了输入时是按y坐标升序输入的,因此根本不用考虑y, 直接把一个二维的问题变成了一个一维的问题,
题目直接变成了对于某个数X,在已经输入的数中, 有多少个数比它小,这时候直接搞一个线段树就可以了,
线段树的每个区间节点,记录分布在此区间内的节点的数量,一个新节点加入,按照区间向下加就可以了,
比如最初是区间 1 - A, 那么一个新的数,1<= B<=A, 那么区间[1, A] 的节点数量就+1, 同样的继续向下, 如果 B > (1+A)/2, 那么 重新对 [(1+A)/2+1, A]进行处理,
否则,对 [1, (1+A)/2]进行处理,就这样一直到最后到了[B,B]这个区间。
而要查询某个区间[A, C]前面有多少个比B小的数, 同样考虑,M = (A+C)/2, 那么如果 B <=M, 只需考虑[A, B], 如果 B > M 且 A < M, 那么等于 [A, M] + [M, B],
最后如果连A都>M, 那么 只考虑 [A, B]。