线段树+离散化 poj2528

题目链接

1.题目描述


有一面墙,现在需要在墙上贴广告做宣传,有N张广告,输入N组数据,代表每张广告所占的墙的范围。问:把所有广告贴好后,最多能看到多少张广告。(有的广告可能被后面贴的广告所掩盖)
1.N<10000, 墙的范围 < 107

2.思路

a.常规思路:

用一个数组表示一面墙,输入第k广告的范围i~j, 把a[i ~ j] 置为k,最后遍历整个数组,答案就是整数的个数
问题:
1. n太大,每次置为K太耗时。(利用线段树)
2. 墙的范围太大,许多不需要,只要标记就好。(离散化)

b.改进:

考虑到之前的弊端,因此在常规思路的基础上选择用:线段树+离散化
1. 离散化:将所有范围的点进行排序,有一条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]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。

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

3.代码如下

#include<cstdio>  
#include<map>  
#include<set>   
#include<algorithm>  
using namespace std;  
map<int,int>mp;  
set<int>st;   
int cmp(int a,int b)  
{  
    return a<b;  
}  
int a[80010],c[80010],x[10010],y[10010];  
void down(int i)  
{  
     if(c[i])  
     {  
             c[2*i]=c[2*i+1]=c[i];  
             c[i]=0;  
     }  
}   
void build(int i,int l,int r)  
{  
     c[i]=0;  
     if(l==r)  
     return;  
     int m=(l+r)>>1;  
     build(2*i,l,m);  
     build(2*i+1,m+1,r);  
}  
void ud(int i,int l,int r,int L,int R,int x)  
{  
     if(l==L&&r==R)  
     {  
                   c[i]=x;  
                   return;  
     }  
     down(i);  
     int mid=(L+R)>>1;  
     if(mid>=r)  
     ud(2*i,l,r,L,mid,x);   
     else if(mid<l)  
     ud(2*i+1,l,r,mid+1,R,x);   
     else  
     {  
         ud(2*i,l,mid,L,mid,x);   
         ud(2*i+1,mid+1,r,mid+1,R,x);  
     }  
}  
void dfs(int i,int l,int r)         
{  
     if(c[i])  
     {  
             st.insert(c[i]);  
             return;   
     }              
     if(l==r)  
     return;   
     int m=(l+r)>>1;   
     dfs(2*i,l,m);  
     dfs(2*i+1,m+1,r);  
}   
int t,n;  
int main()  
{  
    scanf("%d",&t);  
    while(t--)  
    {  
              int n;  
              scanf("%d",&n);  
              mp.clear();  
              st.clear();   
              int j=0;  
              for(int i=1;i<=n;i++)  
              {  
                      int q,w;  
                      scanf("%d%d",&q,&w);  
                      x[i]=q,y[i]=w;  
                      a[++j]=q;  
                      a[++j]=w;  
              }  
              sort(a+1,a+2*n+1,cmp);  
              int z=0;  
              for(int i=1;i<=2*n;i++)  
              {  
                     while(i!=2*n&&a[i]==a[i+1])i++;  
                     mp[a[i]]=++z;   
              }  
              build(1,1,z);  
              for(int i=1;i<=n;i++)  
              {  
                      ud(1,mp[x[i]],mp[y[i]],1,z,i);  
              }   
              dfs(1,1,z);   
              printf("%d\n",st.size());  
    }  
    return 0;  
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值