hiho 21 线段树 离散化

来源:互联网 发布:php身份证号判断性别 编辑:程序博客网 时间:2024/06/12 21:36

问题描述

之前线段树都是针对离散点的区间进行查找的。这次问题变成了,修改和查找都是在连续的一段区间上进行的。
题目链接

解决方法

首先我们要将连续区间转换为离散区间,即将连续区间的端点排序,相邻端点间的一段作为一个叶子节点,然后建立线段树。
在线段树中每个节点使用lazyTag记录当前段被哪个画报覆盖。每张画报看做对线段树的一次区间修改操作。当所有修改完成后,对线段树进行dfs,统计所有能够看到的画报个数。
这里需要注意,与之前的线段树不同的是,这里的线段树叶子节点表示的是一段线段。比如叶子节点i 表示 [i, i+1]。 划分区间时, 区间[L, R] 应分为[L, mid] 和[mid, R]两段。判断区间是否有重叠也有些不同。

#include <bits/stdc++.h>using namespace std;enum {maxn = 1<<17};set<int> pos;int posToid[maxn<<1];int ma[maxn];int mb[maxn];bool surface[maxn];int st[maxn<<2];// store lazyTag;void pushDown(int rt){    if (st[rt])    {        st[rt<<1] = st[rt<<1|1] = st[rt];        st[rt] = 0;    }}int ul, ur, uv;void update(int rt, int L, int R){    if (ul<= L&& R<= ur)    {        st[rt] = uv;        return;    }    if (R <= ul || ur <= L)// 与连续型判断不同。        return;    pushDown(rt);    int m = L + (R-L)/2;    update(rt<<1, L, m);    update(rt<<1|1, m, R);}void search(int rt, int L, int R){    if (st[rt])    {        surface[st[rt]] = true;        return ;    }    if (L == R-1)        return;    int m = L+(R-L)/2;    search(rt<<1, L, m);    search(rt<<1|1, m, R);}int main(){    memset(surface, 0, sizeof(surface));    memset(st, 0, sizeof(st));    int n, l;    scanf("%d %d", &n, &l);    for (int i=1; i<= n; i++)    {        int a, b;        scanf("%d %d", &ma[i], &mb[i]);        pos.insert(ma[i]);        pos.insert(mb[i]);    }    set<int>::iterator it=pos.begin();    int cnt = 1;    for (; it!= pos.end(); it++)        posToid[*it] = cnt++;    for (int i=1; i<=n; i++)    {        ma[i] = posToid[ma[i]];        mb[i] = posToid[mb[i]];        ul = ma[i];        ur = mb[i];        uv = i;        update(1, 1, pos.size());    }    search(1, 1, pos.size());    int res = 0;    for (int i=1; i<=n; i++)        if (surface[i]) res++;    printf("%d\n", res);    return 0;}

解法二

别人比较简洁代码, 使用map实现

#include <bits/stdc++.h>using namespace std;typedef map<int,int>::iterator it;int main() {    map<int,int> m;    m[INT_MIN] = -1;    m[INT_MAX] = -1;    int n, x, y;    scanf("%d%*d", &n);    while(n--) {        scanf("%d%d", &x, &y);        it l = m.lower_bound(x);        it r = m.upper_bound(y);        it t = r; -- t;        int p = t->second;        for(it i=l; i!=r; ) m.erase(i++);        m[x] = n; m[y] = p;    }    set<int> s;    for(it i=m.begin(); i!=m.end(); ++i) s.insert(i->second);    printf("%d\n", s.size() - 1);    return 0;}

**std::map::lower_bound:**Returns an iterator pointing to the first element in the container whose key is not considered to go before k (i.e., either it is equivalent or goes after).

**std::map::upper_bound:**Returns an iterator pointing to the first element in the container whose key is considered to go after k.

代码理解:
map中每个节点表示:区间[当前节点key,下节点key]能看到的画报是当前节点的val;
这一行

for(it i=l; i!=r; ) m.erase(i++);

这里不能替换为

for(it i=l; i!=r; i++) m.erase(i);

原因是在下面的代码中先将i指向的元素删除,此时i已经是invalid的,再执行i++则会产生运行时错误。
而第一行中i++这一句可以理解为,先做了i=i+1 这个操作,但是i++返回没有加一之前的i, erase会删除没有加1之前i指向的元素。由于i在删除之前已经指向后面,所以不会产生运行时错误。
对i++的解释参考http://blog.csdn.net/galaxy_wolf/article/details/51146758

0 0
原创粉丝点击