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

题目链接:

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

题意:

一个区间贴海报,然后问你在最后,能看见多少个海报

题解:

用每个离散化之后的编号, 区分不同海报, 每次更新都是覆盖前一个值的,最后统计一下有多少不同的v就好了

稍微了解了离散化的姿势:
http://www.cnblogs.com/gongxijun/p/4020322.html

通俗点说,离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变。举个例子:

有一条1到10的数轴(长度为9),给定4个区间[2,4] [3,6] [8,10] [6,9],覆盖关系就是后者覆盖前者,每个区间染色依次为 1 2 3 4。

现在我们抽取这4个区间的8个端点,2 4 3 6 8 10 6 9

然后删除相同的端点,这里相同的端点为6,则剩下2 4 3 6 8 10 9

对其升序排序,得2 3 4 6 8 9 10

然后建立映射

2 3 4 6 8 9 10

↓ ↓ ↓ ↓ ↓ ↓ ↓

1 2 3 4 5 6 7

那么新的4个区间为 [1,3] [2,4] [5,7] [4,6],覆盖关系没有被改变。新数轴为1到7,即原数轴的长度从9压缩到6,显然构造[1,7]的线段树比构造[1,10]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。

离散化时有一点必须要注意的,就是必须先剔除相同端点后再排序,这样可以减少参与排序元素的个数,节省时间。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//
const int maxn = 1e5+10;

vector<int> p;
map<int,int> H;
int flag[maxn];
struct node{
    int l,r,v; // l,r 表示这个节点管辖的范围, v表示海报离散化之后的编号
    void update(int val){
        v = val;  // 这段区间被后面的海报覆盖了
    } 
}tree[maxn<<2];
struct qu{
    int x,y;
}q[maxn];

void pushdown(int rt){
    int lazy = tree[rt].v;
    if(lazy){
        tree[rt<<1].update(lazy);
        tree[rt<<1|1].update(lazy);
        tree[rt].v = 0;  // 传递到孩子了 没有值了
    }
}

void build(int rt,int l,int r){
    tree[rt].l = l, tree[rt].r = r;
    tree[rt].v = 0;
    if(l == r)
        return ;
    int mid = (l+r)/2;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
}

void update(int l,int r,int rt,int va){
    int L = tree[rt].l, R = tree[rt].r;
    if(l<=L && R<=r)
        tree[rt].update(va); //在rt这个范围是一张海报
    else{
        pushdown(rt);
        int mid = (L+R)/2;
        if(l<=mid) update(l,r,rt<<1,va);
        if(r>mid) update(l,r,rt<<1|1,va);
    }
}

void query(int rt,int l,int r){
    int L = tree[rt].l, R = tree[rt].r;
    if(L==R || tree[rt].v!=0){  // 如果到了叶子 就不再往下走了,就算是计算了这个叶子的编号v 可能是0 但是我们最后统计答案的时候是从flag[1~n] 所以无影响
        flag[tree[rt].v] = 1;
        return ;
    }

    pushdown(rt);
    int mid = (L+R)/2;
    if(l<=mid) query(rt<<1,l,r);
    if(r>mid) query(rt<<1|1,l,r);
}

int main(){
    int T = read();
    while(T--){
        H.clear(),p.clear();
        int n = read();
        for(int i=0; i<=n; i++) flag[i] = 0;
        for(int i=0; i<n; i++){
            q[i].x = read(),  q[i].y = read();
            p.push_back(q[i].x);
            p.push_back(q[i].y);
        }
        sort(p.begin(),p.end());
        p.erase(unique(p.begin(),p.end()),p.end());
        for(int i=0; i<(int)p.size(); i++){
            H[p[i]] = i+1;
        }

        build(1,1,p.size());
        for(int i=0; i<n; i++)
            update(H[q[i].x],H[q[i].y],1,i+1);
        query(1,1,p.size());
        int ans = 0;
        for(int i=1; i<=n; i++)
            if(flag[i])
                ans++;
        cout << ans << endl;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值