POJ 2528 Mayor's posters(线段树+离散化)

来源:互联网 发布:易游网络验证山寨 编辑:程序博客网 时间:2024/05/29 16:37

题目大意:

将一些海报贴在一排的瓷砖上,这些海报的高度是一样的,且在贴的过程总保持所有海报的高度一致,海报是完整的贴在每块瓷砖上的(也就是说,如果海报覆盖了一块瓷砖,那就是完全覆盖该瓷砖),先给出张贴海报覆盖瓷砖的左边瓷砖的编号和右边瓷砖的编号(给出的顺序是按照张贴的顺序给出的)。求所有海报都张贴完之后有多少海报是完全或者部分可见的。

解题思路:

最后张贴的那张海报一定是完全可见的。本题用线段是来做,每块瓷砖对用一个线段树中的叶子节点,表示节点的结构体为:

struct CNode {               //线段树中每个结点的结构体定义    int L, R;                //结点所表示的区间[L,R]    bool isCover;            //结点表示的区间是否被海报完全覆盖    CNode *pLeft, *pRight;   //左子树指针和右子树指针};

还要注意的是:因为瓷砖的个数非常多(10,000,000),而海报的个数少(10,000),如果用线段树把瓷砖完整的区间表示出来(即:0~10,000,000),则在程序运行中会出现MLE。所以本题还要对海报的端点进行离散化,海报的个数为N,则顶点的个数最多有(2*N  10^4级别的),用线段树完全可以表示该区间(0~20,000)。

在遍历离散化后的海报区间时,必须按照从最近张贴的海报开始遍历,否则张贴的海报会影响已经张贴的海报的可见情况。

代码:

#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <cstdlib>#define M 1000000using namespace std;struct Poster {              //海报的左边和右边    int L, R;};Poster poster[10100];        //海报的数量struct CNode {               //线段树中每个结点的结构体定义    int L, R;                //结点所表示的区间[L,R]    bool isCover;            //结点表示的区间是否被海报完全覆盖    CNode *pLeft, *pRight;   //左子树指针和右子树指针};CNode Tree[1000000];            //Tree[i]线段树的第i个结点int x[20200], hash[10000010];   //x[]保存所有的海报的端点值,并按从小到大的循序排序并合并重复值                                //hash[]是海报的端点值对应的哈希值int nNodeCount = 0;int Mid(CNode *pNode) {    return (pNode->R + pNode->L) / 2;}void BuildTree(CNode *pRoot, int l, int r) {  //建树,确定每个结点的L和R值、左右子树的指针,                                              //初始化isCover为false:结点表示的                                              //区间没有被海报完全覆盖,    pRoot->L = l; pRoot->R = r;    pRoot->isCover = false;    if(l == r) return ;    nNodeCount ++;    pRoot->pLeft = Tree + nNodeCount;    nNodeCount ++;    pRoot->pRight = Tree + nNodeCount;    BuildTree(pRoot->pLeft, l, Mid(pRoot));    BuildTree(pRoot->pRight, Mid(pRoot)+1, r);}bool Query(CNode *pRoot, int l, int r) {  //每贴一张海报,检查海报的[l,r]是否已经被其他海报完全覆盖    if(pRoot->isCover) return false;      //如果pRoot结点表示的区间(肯定覆盖了[l,r]区间)被完全覆盖,                                          //直接返回false    if(pRoot->L == l && pRoot->R == r) {  //能够走到这段代码,说明[l,r]没有被完全覆盖                                          //如果海报的[l,r]和结点pRoot表示的区间想吻合,更新该结点                                          //的isCover的值。表示该顶点表示的区间被该海报完全覆盖        pRoot->isCover = true;        return true;    }        //[l,r]真包含于[pRoot->L, pRoot->R]    bool Result;    if(r <= Mid(pRoot)) {                       //线段树常用语法,如果(l,r)在pRoot的左子树,则进入左子树        Result = Query(pRoot->pLeft, l, r);    }    else if(l > Mid(pRoot)) {                   //进入右子树        Result = Query(pRoot->pRight, l, r);    }    else {                                      //部分进入左子树,部分进入右子树        bool b1 = Query(pRoot->pLeft, l, Mid(pRoot));        bool b2 = Query(pRoot->pRight, Mid(pRoot)+1, r);        Result = b1 || b2;        //Result = Query(pRoot->pLeft, l, Mid(pRoot)) || Query(pRoot->pRight, Mid(pRoot)+1, r);        //注释的部分是错误的,如果第一个Query()为true,后面的Query()函数就不会执行    }    if(pRoot->pLeft->isCover && pRoot->pRight->isCover)        pRoot->isCover = true;    return Result;}int main() {    int t, nCount;      //nCount=所有海报端点值(l和r)的个数    scanf("%d", &t);    while(t--) {        int n;        scanf("%d", &n);        nCount = 0;        for(int i=0; i<n; i++) {            scanf("%d%d", &poster[i].L, &poster[i].R);            x[nCount++] = poster[i].L;            x[nCount++] = poster[i].R;        }        sort(x, x+nCount);        nCount = unique(x, x+nCount) - x; //合并x[]中重复的元素        int nIntervalNo = 0;        for(int i=0; i<nCount; i++) {     //将每一个x[i]离散化,因为x[i]的值很大(10^7),如果用x[i]的值表示的                                          //区间建树BuildTree(Tree, 0, max(x[i])),则程序在运行过程中会MLE            hash[x[i]] = nIntervalNo;            if(i < nCount - 1) {                if(x[i+1] == x[i]+1)                    nIntervalNo ++;                else nIntervalNo += 2;  //相邻x[i]和x[i+1]的值相差超过1,则忽略两个数过大的差值,将其差值                                        //认为是2,形象一点:如果几张海报都覆盖了连续的几块瓷砖,则任务他们                                        //值覆盖了两个瓷砖,即把该连续的几块瓷砖认为两块瓷砖。            }        }        nNodeCount = 0;        BuildTree(Tree, 0, nIntervalNo);    //建树        int ans = 0;        for(int i=n-1; i>=0; i--) {         //从最近张贴的海报遍历所有的海报。从n-1~0遍历海报可以确保对于已经                                            //遍历过的海报不会受到还没有遍历过的海报的影响            if(Query(Tree, hash[poster[i].L], hash[poster[i].R]))                ans ++;        }        printf("%d\n", ans);    }    return 0;}



0 0