【线段树】POJ - 2528 Mayor's posters

  • 题意 - 选举在即,需要往墙上贴海报,贴的位置为[l,r]给出,且要注意的是墙上的每个节点都为一个段,后贴的海报会覆盖掉之前的海报在两个海报位置相交的部分,最后查询最后墙上能看到几种海报。
  • 分析 - 看起来是线段树的区间更新,但是贴海报的位置坐标范围为1e7,所以以坐标建立线段树是行不通的,我们只需要的是某段区间内海报的有无和种类,不需要知道有海报的范围的长度,更无需知道墙上坐标为Xi的那一段有无海报。所以我们可以通过对点的离散化省去一些无用的信息。如:三段区间内有不同的海报,分别为[1,3]、[5,10]、[1,100]我们发现线段上只有5个点1、3、5、10、100,我们将其分别标号为①、②、……、⑤,那么初始的三段区间则变成了[1,2]、[3,4]、[1,5],那么我们需要存储的东西就从数轴上的每个点变成了有用区间的端点(且会通过操作将其按序从1开始排,但不会影响之前的区间关系,如[1,3]还是变成了[1,2])。
  • 怎么离散化 - 我们先将有用区间的端点都存下,但是可能会有区间i的右端点与区间j的左端点相等的情况,所以我们还需要去重。但是还有一个问题,如区间给出[1,10]、[1,4]、[7,10],若经过上述操作则区间会变成[1,4]、[1,2]、[3,4]得到的结果为2种,但实际答案为3,出现这种情况的原因为离散化之前未直接相邻的数据经过离散化处理变成了直接相邻(4和7变成了2和3),所以二者间的信息并未体现出,我们可以通过直接往4和7中加一个5 将区间划分开,那么处理后区间会变成[1,5]、[1,2]、[4,5]解决了上述问题。
  • 关于范围 - 最坏情况为N个区间都不相交,且相邻区间的间距都大于1,则需要MAXN * 3大小的数组来存储离散化后的点。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 10005;
int n, ans;
int l[MAXN], r[MAXN];
int col[MAXN << 4]; // color
int vrtx[MAXN << 2]; // 离散后的节点
int Hash[MAXN << 2]; 
void PushDown(int rt){
    if(col[rt] != -1){ //若当前节点更新了新的颜色, 则更新它孩子的颜色
        col[rt << 1] = col[rt];
        col[rt << 1|1] = col[rt];
        col[rt] = -1;
    }
}
void Build_Tree(){
    memset(col, -1, sizeof(col)); // col 为 -1 时表示当前节点无色
    memset(Hash, 0, sizeof(Hash)); //Hash[i]存的是颜色i之前是否出现过
}
void UpDate(int L, int R, int c, int l, int r, int rt){
    if(L <= l && r <= R) {
        col[rt] = c;
        return ;
    }
    PushDown(rt);
    int mid = (l + r) >> 1;
    if(L <= mid) UpDate(L, R, c, l, mid, rt << 1);
    if(R > mid) UpDate(L, R, c, mid + 1, r, rt << 1|1);
}
void Query(int l, int r, int rt){
    if(col[rt] != -1){ // 若有色
        if(!Hash[col[rt]]) ans++;
        Hash[col[rt]] = 1;
        return ;
    }
    if(l == r) return ;
    int mid = (l + r) >> 1;
    Query(l, mid, rt << 1);
    Query(mid + 1, r, rt << 1|1);
}
int Binary_Search(int x, int n){ // 二分查找坐标上为x的点离散后的位置(即在数组中的下标)
    int l = 0; int r = n - 1;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(vrtx[mid] == x) return mid;
        else if(vrtx[mid] > x) r = mid - 1;
        else l = mid + 1;
    }
    return -1;
}
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        int virtN = 0, realN = 1; ans = 0;
        // virN 经过离散化处理前的所有点, realN 离散化后最终的节点个数
        scanf("%d", &n);
        for(int i = 0; i < n; i++){
            scanf("%d%d", &l[i], &r[i]);
            vrtx[virtN++] = l[i]; vrtx[virtN++] = r[i]; //存下线段的端点
        }
        sort(vrtx, vrtx + virtN); // 排序以去重
        for(int i = 1; i < virtN; i++)
            if(vrtx[i] != vrtx[i - 1]) vrtx[realN++] = vrtx[i]; // 去掉重复的点
        for(int i = realN - 1; i > 0; i--)
            if(vrtx[i] - vrtx[i - 1] > 1) vrtx[realN++] = vrtx[i-1] + 1;
        //通过往实际不相邻(值相差1)的两个数中间插入一个值来消除离散化后二者相邻的情况
        sort(vrtx, vrtx + realN);
        Build_Tree();
        for(int i = 0; i < n; i++){
            int x = Binary_Search(l[i], realN); //查找要进行区间更新的区间边界
            int y = Binary_Search(r[i], realN);
            UpDate(x, y, i, 0, realN - 1, 1); //更新, 叶子节点从0到realN-1, 即vrtx数组中的有效数的下标范围
        }
        Query(0, realN - 1, 1);
        printf("%d\n",ans);
    }

    return 0;
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liusuHeart/article/details/79979179
个人分类: 数据结构
上一篇【线段树】HDU - 1698 Just a Hook
下一篇【线段树】POJ - 3667 Hotel
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭