• POJ 2528 ——Mayor's posters(线段树+区间操作)


     

    Time limit 1000 ms
    Memory limit 65536 kB

    Description

    The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at all places at their whim. The city council has finally decided to build an electoral wall for placing the posters and introduce the following rules:

    • Every candidate can place exactly one poster on the wall.
    • All posters are of the same height equal to the height of the wall; the width of a poster can be any integer number of bytes (byte is the unit of length in Bytetown).
    • The wall is divided into segments and the width of each segment is one byte.
    • Each poster must completely cover a contiguous number of wall segments.


    They have built a wall 10000000 bytes long (such that there is enough place for all candidates). When the electoral campaign was restarted, the candidates were placing their posters on the wall and their posters differed widely in width. Moreover, the candidates started placing their posters on wall segments already occupied by other posters. Everyone in Bytetown was curious whose posters will be visible (entirely or in part) on the last day before elections.
    Your task is to find the number of visible posters when all the posters are placed given the information about posters' size, their place and order of placement on the electoral wall.

    Input

    The first line of input contains a number c giving the number of cases that follow. The first line of data for a single case contains number 1 <= n <= 10000. The subsequent n lines describe the posters in the order in which they were placed. The i-th line among the n lines contains two integer numbers l i and ri which are the number of the wall segment occupied by the left end and the right end of the i-th poster, respectively. We know that for each 1 <= i <= n, 1 <= l i <= ri <= 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered l i, l i+1 ,... , ri.

    Output

    For each input data set print the number of visible posters after all the posters are placed.

    The picture below illustrates the case of the sample input.
    (图片上传失败了,呜呜呜)

    Sample Input

    1
    5
    1 4
    2 6
    8 10
    3 4
    7 10

    Sample Output

    4

    题目分析

           题目要求在一个长为1000,0000的墙上贴海报,但是后面贴上的海报可以覆盖原来已经有了的海报,求贴上一定数量的海报后,还有多少张海报可以被看见(只需要有一小部分可以看见即可,不需要完全看见)。

           首先看到这个题,先被这个长度为1000w的墙吓到了,这要是像平时那样直接用这个区间来建树的话,会不会暴内存呀!对于这一点看了别人的写法后,发现他们都对这个区间进行了压缩(似乎叫做离散化),具体做法就是对每个区间的端点进行排序,将小的端点放在前面,这样一来不仅让我们用了建树的区间缩小到了80w,而且还不改变这些报纸所在区间的相对位置,这个方法很值得记下来,嗯。

           说了一个优化空间的方法,但是没有解决这个题目的算法还是无法AC(这个说法似乎有点傻......),那么对于这个题,我们肯定是使用线段树来写的,而且还需要进行线段树的区间更新,而我们需要在每一个区间内维护的信息则是这个区间内有哪一张海报,用flag记录海报的序号(因为海报是按照输入顺序贴上墙壁的),对于有多个海报同时存在的区间,这个区间的flag = 0,所以,每贴一张海报,我们就需要更新一次这张海报所占的区间的flag。

          现在具体讲一下代码的具体实现情况(也可以说是线段树区间更新的原理):

           每贴一张海报我们就更新一次这张海报所占的区间,用尽量大的区间来表示这张海报所占的区间,这样可以用最少的区间来表示要修改的区间,这也是线段树为什么运算速度快的原有,对于这些用来表示海报所占用区间的区间,他们的整个区间上肯定是这张海报所在的区间,所以将这个区间的flag记作这张海报的编号(只对这个区间的flag进行标记,而不对其子区间)。

            当下一张海报开始更新的时候,更新到了某一个区间[nl,nr],如果这个区间完全处在这张海报所在的区间内,那么直接将这个区间的flag设置为这张海报即可(当然,如果这个区间原来的flag != 0 ,那么就相当于直接覆盖原来的,然后这个区间的海报只有现在这张海报);

            而如果这个区间并不完全处于这个海报所在的区间内,而且这个区间的flag != 0 ,也就是之前的海报完全覆盖了这个区间,并且新的海报有一部分处于这个区间内,这样的话,就说明这个区间同时存在了两种海报,这样的话我们就需要将这个区间原来的flag 分配给其左右区间——因为这个区间不完全属于新的海报,又因为这个区间不是完全处于这个海报所在的区间内,所以要寻找这个区间的子区间,直到这个区间的某几个子区间可以表示这张海报所在的区间为止

             因为要访问当前区间的子区间,而且左右区间是即将访问的区间,所以在访问当前区间的左右区间之前(可能访问),我们需要将属于这个区间的flag分配给其左右区间,而将自己的flag归零(这里flag就是相当于lazy的存在,也就是没有将整个区间的flag分配下去,而是在即将访问某个区间之前将需要访问区间应有的flag从其当前区间分配下来,这个地方就是线段树区间更新的精髓所在,我理解为对线段树的单点更新使用线段树),这样一来,不仅让当前区间的flag = 0 , 表示当前区间没有完全处在单独一张海报之下,而且将左右区间的flag分配了下去,这样就让没有被当前海报覆盖的区间的flag仍保持原来的flag

            通过以上的方式就可以保证flag != 0 的区间完全包含某一种海报,而且flag的值就代表海报的编号(这里采用的是海报的输入顺序作为编号)

            最后,我们只需要对每一个区间进行查询,判断某一个区间是否完全包含某一种海报,并用vis[x]记录海报x的出现。

            在这里,我把线段树的区间更新的原理讲了一遍,可能讲的很不好,但我希望能对各位有所帮助。

    代码区

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<sstream>
    #include <iterator>
    #include<cmath>
    #include <map>
    #define sf(a) scanf("%d",&a)
    #define sff(a,b) scanf("%d%d",&a,&b)
    #define sfff(a,b,c) scanf("%d%d%d",&a,&b,&c)
    #define sffff(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
    #define slf(a) scanf("%lld",&a)
    #define ssf(str) scanf("%s",str)
    #define scf(ch) scanf("%c",&ch)
    #define mem(s,data) memset(s,data,sizeof(s))
    #define forL(i,a, b) for (int i = a; i <= b; i++)
    #define forL(j,a, b) for (int j = a; j <= b; j++)
    #define forR(i,a, b) for (int i = a; i >= b; i--)
    #define forR(j,a, b) for (int j = a; j >= b; j--)
    #define ltree(num) num<<1
    #define rtree(num) num<<1|1
    #define inf 0x3f3f3f3f
    using namespace std;
    const int max2 = 100 + 10;
    const int max3 = 1000 + 10;
    const int max4 = 10000 + 10;
    const int max5 = 100000 + 10;
    const int max6 = 1000000 + 10;
    typedef long long ll;
    
    struct Node
    {
        int l;
        int r;
        int flag;        //记录这个区间中含有第几张海报
    }node[8*max4];        //区间大小为2*max4左右,所以结点的最大编号为其四倍(听大神说,这样可以不越界)
    
    bool vis[2 * max4];    //vis[x]记录海报x是否可见
    
    //初始化压缩后的区间
    void build(int nl,int nr,int num)
    {
        node[num].l = nl;
        node[num].r = nr;
        node[num].flag = 0;
        if (nl == nr)
            return;
        const int mid = (nl + nr) >> 1;
        build(nl, mid, num << 1);
        build(mid + 1, nr, num << 1 | 1);
    }
    
    //将这个区间的海报分配给下一个要查询的区间
    void flag_release(int num)
    {
        if(node[num].flag)
        {
            node[num << 1].flag = node[num << 1 | 1].flag = node[num].flag;
            node[num].flag = 0;
        }
    }
    
    void upData(int fl,int fr,int id,int num)            //[fl,fr]代表海报所占区间(可能是子区间),id表示是第几张海报,num是当前区间的编号
    {
        if(fl <= node[num].l && node[num].r <= fr)    //当前区间完全落在查询区间内,就是找到尽量大的区间来表示查询区间
        {
            node[num].flag = id;            //记录这个区间代表的海报是第几个
            return;
        }
        flag_release(num);
        const int mid = node[num].l + ((node[num].r - node[num].l) >> 1);
        if (fl <= mid) upData(fl, fr, id, num << 1);    //需要更新的区间有在当前区间的左区间的部分
        if (fr > mid) upData(fl, fr, id, num << 1 | 1);    //需要更新的区间有在当前区间的右区间的部分
    }
    
    void query(int num)
    {
        if ((node[num].l == node[num].r) || node[num].flag)    //叶子节点或者整个区间均在某一张海报之下
        {
            vis[node[num].flag] = true;                    //如果这个叶子结点可以看见海报,也就是flag != 0,那么就说明这个海报出现了
                                    //否则只会将vis[0]改为true,意味着墙上还有空位
            return;
        }
        const int mid = (node[num].l + node[num].r) >> 1;
        query( num<<1);
        query( num << 1 | 1);                             //查找所有区间
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t--)
        {
            mem(vis, 0);                //初始化目前没有海报可见,毕竟海报还没有贴上去
            int k;
            scanf("%d", &k);
            int tx[max4], ty[max4];                //记录各个海报的位置
            int n = 0;                    //记录压缩后的区间的长度
            int a[2*max4];                //记录各个区间端点
            for (int i = 1; i <= k;i++)
            {
                scanf("%d%d", tx + i, ty + i);
                a[n++] = *(tx + i);
                a[n++] = *(ty + i);
            }
            sort(a, a + n);
            //区间压缩
            for (int i = 1; i <= k;i++)
            {
                tx[i] = (lower_bound(a, a + n, tx[i]) - a) + 1;
                ty[i] = (lower_bound(a, a + n, ty[i]) - a) + 1;
            }
    
            build(1, n, 1);                    //建树并初始化
            for(int i = 1 ; i <= k ; i++)
            {
                upData(tx[i], ty[i], i, 1);        //加上第i张海报后的更新
            }
            query(1);                    //访问所有的区间,查看海报的出现情况
            int sum = 0;
            for(int i = 1 ; i <= k ; i++)
            {
                if (vis[i])
                    sum++;
            }
            cout << sum << endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    软件工程之旅开始啦
    c# async,await, 委托函数
    mysql 访问不是本地数据库,给用户刷新了权限没有作用
    c# WndProc事件 消息类型
    sql not in 优化问题
    c# dataGridView 表头格式设置不管用
    sql 更新多条记录
    mysql 插多行数据
    win7 64bit+vs2010 操作注册表
    bat脚本命令
  • 原文地址:https://www.cnblogs.com/winter-bamboo/p/10634451.html
Copyright © 2020-2023  润新知