POJ 2528(线段树 区间更新 离散化)

本文介绍了解决POJ2528问题的方法,该问题是关于在一系列墙面上贴海报并计算露出表面的海报数量。通过离散化处理坐标范围,利用线段树进行区间更新与查询,最终统计答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

POJ 2528

题目大意

有一排分成若干组长为1区间的墙,现在要往墙上贴n张海报,一个海报覆盖连续的L到R的区间,按照一定的顺序贴,问最后有多少海报全部或有一部分漏在了表面。 (1LR107,n10000)

分析

由于L和R比较大但线段数目不大(最多出现20000种数字),所以需要先对数据进行一下离散化,将L和R映射到一个较小的区间.
更新操作是将一个区间全部更改这个线段的编号,这样在进行n此更新后,对每一个点进行查询看这个点属于哪个线段,最后统计以下就是答案。

技巧

(以下内容引用自:数据的离散化)
使用STL算法离散化:
思路:先排序,再删除重复元素,然后就是索引元素离散化后对应的值。
假定待离散化的序列为a[n],b[n]是序列a[n]的一个副本,则对应以上三步为:

sort(sub_a,sub_a+n);
int size=unique(sub_a,sub_a+n)-sub_a;//size为离散化后元素个数
for(i=0;i<n;i++)
    a[i]=lower_bound(sub_a,sub_a+size,a[i])-sub_a + 1;//k为b[i]经离散化后对应的值

对于第3步,若离散化后序列为0, 1, 2, …, size - 1则用lower_bound,从1, 2, 3, …, size则用upper_bound,其中lower_bound返回第1个不小于b[i]的值的指针,而upper_bound返回第1个大于b[i]的值的指针,当然在这个题中也可以用lower_bound然后再加1得到与upper_bound相同结果,两者都是针对以排好序列。使用STL离散化大大减少了代码量且结构相当清晰。

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
const int MAXN=100005;//线段数
int n;
int info[MAXN*4];//线段树维护的信息
int lenth;
int bj[MAXN*2];
int num[MAXN*2];//用于保存离散化信息
int cnt;
using namespace std;
struct LINE
{
     int a;
     int b;
}line[MAXN];
void Init()
{
      cnt=0;
      lenth=0;
      memset(bj,0,sizeof(bj));
      memset(info,0,sizeof(info));
}
void Pushdown(int rt)//下推延迟信息,该节点的信息已更新
{
      if(info[rt])
      {
            info[rt*2]=info[rt];
            info[rt*2+1]=info[rt];
            info[rt]=0;
      }
}
void Update(int L,int R,int C,int l,int r,int rt)//保证(L,R)区间的数将会更新为C
{
    if(L<=l && r<=R){info[rt]=C;return ;}
    int m=(l+r)/2;
    Pushdown(rt);
    if(L<=m)Update(L,R,C,l,m,rt*2);
    if(R>m)Update(L,R,C,m+1,r,rt*2+1);
}
int Query(int x,int l,int r,int rt)//查询第x个数是什么
{
    if(l==r)return info[rt];

    int m=(l+r)/2;
    Pushdown(rt);
    if(x<=m)return Query(x,l,m,rt*2);
    else if(x>m)return Query(x,m+1,r,rt*2+1);
}
void Solve()
{
    int t;
    int ans=0;
    for(int i=1;i<=lenth;i++)
    {
          t=Query(i,1,lenth,1);
          bj[t]=1;
    }
    for(int i=1;i<=lenth;i++)ans+=bj[i];
    printf("%d\n",ans);
}
int main()
{
    int T;
    int a,b;
    scanf("%d",&T);
    while(T--)
    {
          Init();
          scanf("%d",&n);
          for(int i=1;i<=n;i++)
          {
              scanf("%d%d",&a,&b);
              num[++cnt]=a;
              num[++cnt]=b;
              line[i].a=a;line[i].b=b;
          }
          sort(num+1,num+cnt+1);
          int m=unique(num+1,num+cnt+1)-num-1;//unique返回去重后尾地址,但那个地址不含数字,m表示去重后的个数
          int t=m;
          for(int i=2;i<=t;i++)//如果两个数相差大于1则在他们之间插入一个数
          {
                if(num[i]-num[i-1]>1)
                      num[++m]=num[i-1]+1;
          }
          sort(num+1,num+m+1);
          int x,y;
          lenth=m;
          for(int i=1;i<=n;i++)
          {
               x=lower_bound(num+1,num+m+1,line[i].a)-num;//lower_bound返回第1个不小于line[i].a的值的指针
               y=lower_bound(num+1,num+m+1,line[i].b)-num;
               Update(x,y,i,1,lenth,1);
          }
          Solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值