POJ 2528 Mayor's posters 线段树区间更新+端点离散化

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 li 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 li ri 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered li , li+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.

http://poj.org/problem?id=2528

Sample Input

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

Sample Output

4

题意:

有一面长度为10000000的墙,往上面先后贴10000张海报。把墙看成10000000块,第 i 份海报将覆盖第 li ~ ri 块区域,问最后有多少张海报还有露出来的部分

分析:
线段树的好处就是,不用每次输入都更新所有的结点的状态,而是把需要做的更改暂时交给它们的父亲结点(区间结点)保管,等到以后某一次输入或访问,必须访问到它本身的时候,我们再从它父亲结点以很低的代价生成它的最新状态。以这道题为例,在反复涂刷颜色的过程中,有些颜色最后不会出现,把它们更新到子结点毫无意义,只用区间节点暂存一下就行了。
离散化,只关心露出来的海报个数,不关心长度,所以可以把端点按大小排序后,依次压缩,比如两张海报分别是[1,5]和[2,7],压缩后为[1,3]和[2,4] 1 ,并不影响他们之间的关系。另外,网上还有文章提到类似于[3,9] [1,4] [6,11]三张海报压缩后变成[2,5] [1,3] [4,6] 2 ,第一张海报[3,9]露出来的部分长度被离散成0的情况,我只能说,细还是大佬细啊Orz。
注1 [1,5] [2,7] 排序后为1, 2, 5, 7 离散后分别对应1, 2, 3, 4
注2 [3,9] [1,4] [6,11] 排序后为 1, 3, 4, 6, 9, 11 离散后分别对应1, 2, 3, 4, 5, 6

离散化的方法参考了下面这位巨巨的代码

poj2528–Mayor’s posters(线段树+离散化)http://blog.csdn.net/winddreams/article/details/38443761

#include <iostream>
#include<algorithm>
#include<string.h>
using namespace std;

#define maxn 10000
//定义宏方便找到结点i的父亲以及左右子结点
#define fath(i) (i>>1)
#define left(i) (i<<1)
#define right(i) ((i<<1)+1)
//计算区间l,r的中点
#define mid(l,r) ((l) + (((r) -(l)) >> 1))

//利用一个类来离散端点
class trans {
public:
    //两个id分别记录原来的顺序,和离散后的顺序
    int id1;
    int id2;
    //k记录端点的值
    int k;
}dis[(maxn << 1) + 5];

//按id1排序
bool cmp1(trans a1, trans a2) {
    return a1.id1<a2.id1;
}
//按id2排序--没有用到
bool cmp2(trans a1, trans a2) {
    return a1.id2<a2.id2;
}
//按k排序
bool cmpk(trans a1, trans a2) {
    return a1.k<a2.k;
}

//统计有哪些海报露出来的数组[1出现/0未出现]
int cnt[maxn + 5] = { 0 };
//露出来的海报个数
int ans = 0;

//线段树结点
class xds
{
public:
    xds() :l(0), r(0), top(0), lzy(0), blt(false) {}
    //该结点维护的区间
    int l;
    int r;
    //该结点是否在最顶端,也就是区间内是否只有一种颜色
    int top;
    //是否暂存了子结点的改变
    int lzy;
    //是否生成过子结点
    bool blt;
}node[(maxn << 3) + 5];//只开maxn<<2 会runtime error,有大佬能解释下原因吗

void upd(int i);
void cov(int cl, int cr, int top, int i);

//生成结点i的左右子树的最新状态,也就是把lzy向下更新
void upd(int i) {
    if (node[i].l != node[i].r)//判断i是不是叶子结点
    {
        node[left(i)].top = node[right(i)].top = node[left(i)].lzy = node[right(i)].lzy = node[i].lzy;
    }
    //更新过后记得把lzy置0
    node[i].lzy = 0;
}

//更新覆盖状态,把“[cl,cr]现在被top代表的报纸覆盖”这个信息更新到树上,从第i个结点开始
void cov(int cl, int cr, int top, int i) {
    int l = node[i].l;
    int r = node[i].r;

    //匹配的区间,跳过对子树的更新,并标记lzy
    if (cl == l&&cr == r) {
        node[i].top = top;
        node[i].lzy = top;
        return;
    }

    //需要进入子结点

    //检测是否生成了子结点
    if (node[i].blt == false)
    {
        node[left(i)].l = node[i].l;
        node[left(i)].r = mid(node[i].l, node[i].r);
        node[right(i)].l = node[left(i)].r + 1;
        node[right(i)].r = node[i].r;
        node[i].blt = true;
    }

    //检测是否有lzy还没更新
    if (node[i].lzy)upd(i);

    //需要进入子结点意味着当前不止一种颜色
    node[i].top = -1;

    //只更新i所维护的区间的左边,进入左子树
    if (cr <= mid(l, r)) {
        cov(cl, cr, top, left(i));
        return;
    }

    //i所维护的区间左右两边都要更新,左右子树都要进入
    if (cl <= mid(l, r)) {
        cov(cl, mid(l, r), top, left(i));
        cov(mid(l, r) + 1, cr, top, right(i));
        return;
    }
    //只更新i所维护的区间的右边,进入右子树
    if (cl > mid(l, r)) {
        cov(cl, cr, top, right(i));
        return;
    }

}

//数露出来的海报
void sol(int i) {
    //如果这个区间下颜色唯一,就把这个颜色加入判断数组cnt
    if (node[i].top >= 0) {
        if (cnt[node[i].top] == 0)
        {
            ++ans;
            cnt[node[i].top] = 1;
        }
        return;
    }
    //有多种颜色,需要进入子结点
    sol(left(i));
    sol(right(i));

}

int main() {
    int kase;
    int n;
    int nd;
    cin >> kase;

    //输入数据,并把序号计入id1
    cin >> n;
    for (int i = 0; i< n; ++i) {
        dis[i].id1 = i + 1;
        dis[i + n].id1 = i + n + 1;
        cin >> dis[i].k >> dis[i + n].k;
    }
    //按k排序
    sort(dis, dis + (n << 1), cmpk);

    //将离散化后的序号计入id2
    int t1 = 1, t2 = 1;
    dis[0].id2 = 1;
    //nd为离散后的结点个数
    nd = 1;
    for (int i = 1; i< (n << 1); ++i) {
        //如果与前一个相同,则共用一个离散后的端点,nd就不+1
        if (dis[i].k == dis[i - 1].k)dis[i].id2 = nd;
        /*else if (dis[i].k - dis[i - 1].k > 1) { 
        }*///题目没考虑到的情况
        //与前一个不同,nd++
        else dis[i].id2 = ++nd;
    }
    //按id1排序,恢复输入顺序
    sort(dis, dis + (n << 1), cmp1);

    //初始化根结点
    node[1].top = 0;
    node[1].l = 1;
    node[1].r = nd;
        //更新覆盖信息
        for (int i = 0; i < n; ++i) {
            //顺便初始化解答数组
            cnt[i] = 0;

            int cl = dis[i].id2;
            int cr = dis[i + n].id2;
            cov(cl, cr, i + 1, 1);
        }
        cnt[n] = 0;
        ans = 0;
        //从根结点开始数
        sol(1);

        cout << ans << endl;
        //清空树
        memset(node, 0, sizeof(node));

    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值