poj-2528

// 20884K   329MS   G++
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int posterNum;

int ep[20010];

unsigned short dis[10000010];
int epNum;

int cmp(const void * p1, const void * p2) {
    return (*((int*)p1)) - (*((int*)p2));
}

struct PosterRange {
    int begin;
    int end;
};

typedef struct PosterRange PosterRange;

PosterRange posterInfo[10005];

int endMax;

struct TreeNode {
    int posterId; // -1, no poster in this range yet, -3, one poster here but not cover whole range. -2, some posters here, but not cover this range. >=0 , only 1 poster occupy here
};

typedef TreeNode TreeNode;

TreeNode treeHead[200050];

void updateTree(int curNodeId, int posterId, int posterBegin, int posterEnd,
                int rangeBegin, int rangeEnd) {

    int mid = (rangeBegin + rangeEnd)>>1;

    if (posterBegin > posterEnd) {
        return;
    }

    if ((posterBegin > rangeEnd) || (posterEnd < rangeBegin)) {
        return;
    }

    int curPosterId = treeHead[curNodeId].posterId;

    if ((posterBegin <= rangeBegin) && (posterEnd >= rangeEnd)) {// whole rage overred by poster
        treeHead[curNodeId].posterId = posterId;
    } else if ((posterBegin == posterEnd) && (rangeBegin == rangeEnd)) { // only one block
        treeHead[curNodeId].posterId = posterId;
    } else if (curPosterId == -1) { // first time occupy, but not whole cover this range
        treeHead[curNodeId].posterId = -3;
        if (posterEnd <= mid) { // whole range in left part
            updateTree(curNodeId<<1, posterId, posterBegin, posterEnd, rangeBegin, mid);
        } else if ((posterBegin <= mid) && (posterEnd >= mid+1)) { // range in both parts
            updateTree(curNodeId<<1, posterId, posterBegin, mid, rangeBegin, mid);
            updateTree(curNodeId<<1|1, posterId, mid+1, posterEnd, mid+1, rangeEnd);
        } else if (posterBegin >= mid + 1) { // whole range in right part
            updateTree(curNodeId<<1|1, posterId, posterBegin, posterEnd, mid+1, rangeEnd);
        }
    } else { // if has been occupied before, and current poster can not cover whole range 
        int prevPosterId = treeHead[curNodeId].posterId;
        treeHead[curNodeId].posterId = -2;

        if (prevPosterId >=0) { // if this range is whole covered by prev color
            treeHead[curNodeId<<1].posterId = prevPosterId;
            treeHead[curNodeId<<1|1].posterId = prevPosterId;
        }

        if (posterEnd <= mid) { // whole range in left part
            updateTree(curNodeId<<1, posterId, posterBegin, posterEnd, rangeBegin, mid);
        } else if ((posterBegin <= mid) && (posterEnd >= mid+1)) { // range in both parts
            updateTree(curNodeId<<1, posterId, posterBegin, mid, rangeBegin, mid);
            updateTree(curNodeId<<1|1, posterId, mid+1, posterEnd, mid+1, rangeEnd);
        } else if (posterBegin >= mid+1) { // whole range in right part
            updateTree(curNodeId<<1|1, posterId, posterBegin, posterEnd, mid+1, rangeEnd);
        }
    }
    // printf("%d %d %d %d %d %d\n", curNodeId, posterBegin, posterEnd, rangeBegin, rangeEnd, treeHead[curNodeId].posterId);
}

int getPosterShowNum(int curNodeId) {
    int curPosterId = treeHead[curNodeId].posterId;
    // printf("get curPosterId:%d %d\n", curNodeId, curPosterId);
    if (curPosterId == -1) { // no poster occupy yet
        return 0;
    } else if (curPosterId >= 0) { // only 1 poster occupy.
        if (!ep[curPosterId]) {
            ep[curPosterId] = 1;
            return 1;
        } else {
            return 0;
        }
    } else if (curPosterId == -2 || curPosterId == -3) { // more than 1 poster occupy this range
        int leftNum = getPosterShowNum(curNodeId<<1);
        int rightNum = getPosterShowNum(curNodeId<<1|1);
        return leftNum + rightNum;
    }
    return 0;
}

int main() {
    int caseNum;
    scanf("%d", &caseNum);
    for (int i = 0; i < caseNum; i++) {
        scanf("%d", &posterNum);
        int posterBegin , posterEnd;
        memset(ep, 0, sizeof(ep));
        memset(dis, 0, sizeof(dis));
        memset(posterInfo, 0, sizeof(posterInfo));
        epNum = 0;
        endMax = 0;

        for (int i = 0; i < posterNum; i++) {
            scanf("%d %d", &posterBegin, &posterEnd);
            posterInfo[i].begin = posterBegin;
            posterInfo[i].end = posterEnd;
            if (!dis[posterBegin]) {
                ep[epNum++] = posterBegin;
                dis[posterBegin] = 1;
            }
            if (!dis[posterEnd]) {
                ep[epNum++] = posterEnd;
                dis[posterEnd] = 1;
            }
        }

        qsort(ep, epNum, sizeof(int), cmp);

        endMax = epNum;
        for (int i = 0; i < 200050; i++) {
            treeHead[i].posterId = -1;
        }

        memset(dis, 0, sizeof(dis));
        for (int i = 0; i < epNum; i++) {
            dis[ep[i]] = i + 1;
        }

        for (int i = 0; i < posterNum; i++) {
            int dPosterBegin = dis[posterInfo[i].begin];
            int dPosterEnd = dis[posterInfo[i].end];
            // printf("A\n");
            updateTree(1, i, dPosterBegin, dPosterEnd, 1, epNum);
        }
        memset(ep, 0, sizeof(ep));
        int showPosterNum = getPosterShowNum(1);
        printf("%d\n", showPosterNum);
    }
}


Alberta Collegiate Programming Contest 2003.10.18

http://ugweb.cs.ualberta.ca/~acpc/2003/

Problem G

线段树的精髓是,能不往下搜索,就不要往下搜索,尽可能利用子树的根的信息去获取整棵子树的信息。如果在插入线段或检索特征值时,每次都非要搜索到叶子,还不如直接建一棵普通树更来得方便。

一个严谨的题解

http://blog.csdn.net/niuox/article/details/9073367

我下边最初的解题思路其实算是不对的,不严谨,没有染过色的区间,如果第一次染色,就把整个区间给染上这个色,是有问题的:

比如

区间 [1 4], 如果最开始来一个 [4 4 1(颜色)] ,那么 区间 [1 4]直接变为1, 如果再来一个 [3 4 2], 那么 因为pushdown,

[1 2] 和 [3 4]会先变成 1,  然后 [3 4 2] 覆盖 [3  4]为2, [1 4]为 -2(混色),最后就变为:

[1 4]  = -2,  [1 2] = 1 [3 4] =2, 这样最后统计会有两种颜色,是错误的,原因就是pushdown 把本来不属于 1 的 [1 2]给染成 1了,所以错误,

因此下面的思路其实有问题,还是应该按照上面的题解严谨操作,每次只有保证完全覆盖某个区间,才将区间染色,否则递归子区间(知道找到完全覆盖子区间为止),这样做,保证了pushdown不会出现上面的问题(因为是覆盖整个区间,因此必然子区间也被覆盖)就是最后求颜色数量时,

可能会走的比较深,但是是正确的.


唉, 虽然是线段树水题,但是对于不怎么熟悉的人来说,真费劲,并且虽然这个程序AC了,但是其实并不正确,此题AC的很多人应该都是通不过这组数据的:

2
3
5 6
4 5
6 8
3
1 10
1 3
6 10
结果应该是 2, 3 但是很多人是2,2,  是因为离散化没有处理妥当.

第二个case中的原始数据: 1 3 6 10 离散化以后对应为 1 2 3 4,

那么输入数据也就变为:

1 4

1 2

3 4

从这组数据看,1 4完全被 1 2 和 3 4完全覆盖了(这里也要对题意理解正确,题目给的每个数字对应的是wall的某一个block, 比如 5 6 对应的就是第5 和 第6块block,

其实是两个block,而不是从5到6的这一个block, 因此本题的线段树是最后可能要化成点的,及 5,5 6,6 分别都对应两个单独block),但其实 2 和 3中间是有空缺的,1 到 4的poster在这段空缺中是可以看到的,所以在离散化是要考虑这种特殊情况,mark一下。

这道题算是线段树的基础应用,本质是经典的着色问题,那么对于线段树的每段区间,要记录一个当前的颜色值(在本题,就是不同的posterId)

-1表示区间R还从来没有被着过色,

>=0表示此区间目前只有这一种颜色,并且是完全覆盖此区间的。

-3表示此区间之存在一种颜色,但是没有完全覆盖此区间.

-2表示此区间当前存在不止一种的颜色,。


本题同时还要求进行离散化,不但能节省空间,还可以节省时间(因为等于把整个区间的范围也给压缩了,比如从 1 10000 压到 1 100, 那么整个线段树的树高就变小了,自然时间也提上去了),离散化就是先收集所有出现的数字(注意搞一个bitmap来排除重复出现的数字),然后进行排序,最后根据其次序进行一一映射,

比如 1 3 6 10

就对应了 1 2 3 4,

然后就是不断的插入颜色的范围和颜色值来构建最终的线段树了(线段树其实很灵活,每个treenode记录不同的信息就可以解决不同的问题,像一个良好的模版方案)

还是直接开了一个大数组来模拟树,之前试着每次malloc treenode,果断的TLE了, 经过离散化以后,这个大数组的尺寸就在题目接受范围内了(最多20000个不同数,那么么离散范围就是 1~ 20000, 大数字只需要 40000)。

插入颜色(b(起始), e(终点), i(颜色值)) 到 区间 (rB, rE)时,会有这些case:

首先要判断区间,如果(rB, rE)被 (b,e) 完全覆盖,那么将直接将此区间染色返回即可.

然后:

[1] 如果区间的颜色值 C == -1, 那么说明此区间还没有被染色,直接修改此区间颜色值为 -3(此区间有一种颜色,但是没有完全覆盖), 同时,继续向下走:

<1> 如果 (b,e) 在 (rB,rE)的左半部分,那么递归的将 (b, e, i) 插入到 (rB, (rB+rE)/2)

<2> 如果 (b,e) 在 (rB, rE)的右半部分,那么递归的将 (b,e,i) 插入到((rB+rE)/2+1, rE)中(注意是,(rB+rE)/2+1, 因为要考虑每个点)

<3>如果(b,e) 横跨了(rB, rE), 那么两次插入: (b, (rB+rE)/2, i) -> (rB, (rB+rE)/2)         ((rB+rE)/2+1,  e, i) ->((rB+rE)/2+1, rE),

一直这样,直到最后找到一个区间,  (b, e) 可以完全覆盖(rB,rE)。

[2] 如果区间的颜色C>=0. 那么说明此区间之前已经有完全覆盖的染色了,那么此区间颜色变为-2,表示此区间有多余一种的颜色,让此区间的左右child区间继承此区间的颜色, 然后和上面的分析一样,进行向下插入。

[3]如果区间的颜色C == -2/-3, 那直接向下进行插入可以了。

最后统计颜色值时,

从线段树的root开始递归的求getNum(),

如果其C==-1 直接return 0,

如果 C>=0,return 1

如果 C==-2/-3, 那么 return getNun(leftChild) + getNum(rightChild)

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