poj2528贴海报(线段树离散化)

//poj2528贴海报(线段树离散化)
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005; //要开10倍的数组,否则RE
bool hash[maxn];
int li[maxn],ri[maxn];
int cov[maxn<<4];
int a[maxn*3];
int cnt;
int BinSea(int key,int n) //二分查找所在的编号即离散后的位置号
{
    int l=0,r=n-1;
    while(l<=r){
        int mid=(l+r)/2;
        if(a[mid]==key) return mid;
        else if(a[mid]<key) l=mid+1;
        else r=mid-1;
    }
    return -1;
}
void Pushdown(int p) //将该区间的标记向子女节点传递
{
    if(cov[p]!=-1){//如果被标记
        cov[p<<1]=cov[p<<1|1]=cov[p];
        cov[p]=-1;
    }
}
/*
void Build(int p,int l,int r)
{
    cov[p]=-1;
    if(l==r) {return;}
    int mid=(l+r)/2;
    Build(p<<1,l,mid);
    Build(p<<1|1,mid+1,r);

}
*/
void Update(int p,int l,int r,int x,int y,int c)
{
    if(x<=l&&y>=r){
        cov[p]=c;
        return;
    }
    Pushdown(p);
    int mid=(l+r)/2;
    if(x<=mid) Update(p<<1,l,mid,x,y,c);
    if(y>mid) Update(p<<1|1,mid+1,r,x,y,c);

}

void  Query(int p,int l,int r) //遍历每个区间的颜色
{
    if(cov[p]!=-1){
        if(!hash[cov[p]]){
            //cout<<cov[p]<<'*'<<endl;
            cnt++;
            hash[cov[p]]=true;//避免重复计数
        }
        return;
    }
    if(l==r) return;
    int mid=(l+r)/2;
    Query(p<<1,l,mid);
    Query(p<<1|1,mid+1,r);
}

int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        int k=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&li[i],&ri[i]);
            a[k++]=li[i];//a[]存端点
            a[k++]=ri[i];
        }
        sort(a,a+k); //排序以便离散化
        int h=1;
        for(int i=1;i<k;i++) //删去相同的数
        {
            if(a[i]!=a[i-1]) a[h++]=a[i];
        }
        for(int i=h-1;i>0;i--) //距离大于1的点之间加一个数
        {
            if((a[i]-a[i-1])>1) a[h++]=a[i-1]+1;
        }
        sort(a,a+h);
        memset(cov,-1,sizeof(cov));//初始标记为-1
        for(int i=0;i<n;i++)
        {
            int l=BinSea(li[i],h);
            int r=BinSea(ri[i],h);
           // cout<<l<<' '<<r<<endl;
            Update(1,0,h-1,l,r,i); //更新该区间包含颜色
        }
        cnt=0;
        memset(hash,false,sizeof(hash));
        Query(1,0,h-1);
        printf("%d\n",cnt);
    }
    return 0;
}
int binsea(int key,int n){
    int l=0,h=r-1;
    while(l<=r){
        int mid=(h+l)>>1;
        if(a[mid]==key) return mid;
        if(a[mid>key]) l=mid+1;
        else if(a[mid]<=key) r=mid-1;
    }
    return -1;
}
void down(int rt){
    if(lazy[rt]!=-1){
        lazr[rt<<1]=lazy[rt<<1|1]=laz[rt];
        lazy[rt]=-1;
    }
}
void update(int rt,int l,int r,int x,int y,int c){// Update(1,0,h-1,l,r,i); c是颜色种类
    if(x<=l&&r<=y){//[l,r]属于[x,y]区间  覆盖掉[l,r]区间  [x,y]是题目给的
        lazy[rt]=c;return;
    }
    down(rt);
       int mid=(l+r)/2;  
    if(x<=mid) Update(p<<1,l,mid,x,y,c);  
    if(y>mid) Update(p<<1|1,mid+1,r,x,y,c);  
}
void query(int rt,int l,int r){//query(1,0,h-1)
    if(lazy[rt]!=-1){
        if(hash[lazy[rt]]==false){//初始值是false  避免重复计数
            cnt++;
            hash[lazy[rt]]==true;
        }
        return;
    }
    if(l==r) return;
    int mid=(l+r)/2;  
    Query(p<<1,l,mid);  
    Query(p<<1|1,mid+1,r); 
    
}

这道题与之前的题目相比重点在于一个映射的预处理,题目所给的区间达到10000000,而最多只有10000个点,如果直接建树的话太过于空旷。把这些区间的左右节点一一对应,最多有4×10000个点,远小于之前的10000000,而且区间之间的对应关系也不会改变。

举个例子: 
区间:[2,6],[4,8],[6,10] 
我们进行下面对应: 
2 4 6 8 10 
1 2 3 4 5 
则原区间变为[1,3],[2,4],[3,5]。可以发现它们之间的覆盖关系并没有改变,但是却紧凑了很多。 
但是注意对应后,应该有一个去重的操作,防止出错。

还有一点去需要注意,区间的对应稍不注意会出现颜色丢失的情况,如下: 
[1,10],–[1,4],–[6,10] 
当我们手工模拟会发现,我们只是对1,4,6,10,进行了对应,即1,2,3,4,原集合中的4,6被视为了相邻元素,所以5处的颜色丢失,最终得到了错误的结果。 
为了防止发生这种情况我们进行插值,在两个不相邻的节点间插入无关值,但是能有效的避免这种情况。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值