poj-2352

// 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]。

你可能感兴趣的:(线段树,poj)