poj2528 Mayor‘s posters 线段树 +离散化

题目链接http://poj.org/problem?id=2528
思路:
每次贴海报,就把目标区间内的颜色标号标记成当前海报颜色标号,最后查询整个墙的分区间内所以不同颜色的数量就可以
但是因为墙的长度太长了,如果直接用线段树,就会内存超限,所以要进行离散化处理,这里是区间的离散化处理,这里我引用了别的博主的话
引用文章https://blog.csdn.net/sugarbliss/article/details/80501556
原来的:1 2 3 4 6 7 8 10 (去重后排序)

映射后:1 2 3 4 5 6 7 8

原来的:[1, 4] [2, 6] [8, 10] [3, 4] [7, 10]

映射后:[1, 4] [2, 5] [7, 8] [3, 4] [6, 8]

很多代码能够AC但是不正确,比如下面的这组数据:

离散化后前:[1, 10] [1, 3] [6, 10]

离散化后是:[1, 4] [1, 2] [3, 4]

离散化前,3与6之间是有间隙的,但是离散化后,2与3相连的,于是原来3与6之间的部分就看不到,少算了,得到的结果是2,而正解是3。

为了解决这个问题,我们可以对排序后的数组处理一下。比如[1, 3, 6, 10]如果相邻数字间距大于1,就在其中加上任意一个数字,比如加成[1, 3, 4, 6, 7, 10, 11],这样处理就好了。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>

using namespace std;

const int maxn = 20000 + 100;

int col_lazy[maxn<<3],vis[maxn],c[maxn<<1];
int cnt;
//col_lazy就是记录当前区间颜色,-1为杂色或者没有海报
//vis就是最后求取总区间内出现过多少不同数字时防止重复计算的标记
//这里要注意maxn,给一万个询问,就是会出现两万个数字,a最坏存4万个,lazy一般是a的4倍,vis只要两万
//a是存离散化数字的数组,就是存海报左右区间中出现过的数字
//cnt答案数量

struct node
{
    int l,r;
}p[maxn];//存未进行离散化前的坐标

//下推标记数组,-1为杂色或者没有海报
void PushDown(int rt)
{
    //如果这个区间只有一种海报颜色,就向下更新
    if(col_lazy[rt] != -1){
        col_lazy[rt<<1] = col_lazy[rt];
        col_lazy[rt<<1|1] = col_lazy[rt];
        col_lazy[rt] = -1;
    }
}

//更新颜色
void Update(int L,int R,int l,int r,int rt,int C)
{
    //把区间内的颜色更新成最新贴的海报颜色
    if(L <= l&&r <= R){
        col_lazy[rt] = C;
        return ;
    }
    int m = (r + l)>>1;
    PushDown(rt);
    if(L <= m){
        Update(L,R,l,m,rt<<1,C);
    }
    if(R > m){
        Update(L,R,m + 1,r,rt<<1|1,C);
    }
}

void Query(int L,int R,int l,int r,int rt)
{
    //如果这个区间有杂色或者没有颜色,那么就向下寻找
    if(L <= l&&r <= R&&col_lazy[rt] != -1){
        if(!vis[ col_lazy[rt] ]){
            /*如果当前海报颜色计算过了,就跳过
            防止一个海报的区间被分割成多个子区间后重复计算*/
            vis[ col_lazy[rt] ] = 1;
            cnt++;
        }
        return ;
    }
    //说明这块空间没有使用,不写就会死循环
    if(l == r){
        return ;
    }
    int m = (l + r)>>1;
    //不需要PushDown函数,因为在Update时就已经将所有状态更新完毕
    if(L <= m){
        Query(L,R,l,m,rt<<1);
    }
    if(R > m){
        Query(L,R,m + 1,r,rt<<1|1);
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        memset(vis,0,sizeof(vis));
        memset(col_lazy,-1,sizeof(col_lazy));
        cnt = 0;
        //tot就是通用的存a个数的变量
        int tot = 1;
        int n;
        scanf("%d",&n);
        for(int i = 1; i <= n; i++){
            scanf("%d%d",&p[i].l,&p[i].r);
            c[tot++] = p[i].l;
            c[tot++] = p[i].r;
        }
        //离散化
        sort(c + 1,c + tot);
        tot = unique(c + 1,c + tot) - c;//防止重复数据干扰
        int k = tot;
        for(int i = 2; i < k; i++){
            if(c[i] - c[i - 1] > 1){
                //区间离散的补充
                c[tot++] = c[i - 1] + 1;
            }
        }
        sort(c + 1,c + tot);
        //二分查找所对应的离散化后的坐标
        for(int i = 1; i <= n; i++){
            int l,r;
            l = lower_bound(c + 1,c + tot,p[i].l) - c;
            r = lower_bound(c + 1,c + tot,p[i].r) - c;
            Update(l,r,1,tot - 1,1,i);
        }
        Query(1,tot - 1,1,tot - 1,1);
        cout<<cnt<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值