poj2528 贴海报

大致题意:

有一面墙,被等分为1QW份,一份的宽度为一个单位宽度。现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1QW。后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报。现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?(PS:看见一部分也算看到。)

 

解题思路:

水题,区间压缩映射(离散化)+ 线段树

 

 

首先建立模型:

给定一条数轴,长度为1QW,然后在数轴上的某些区间染色,第i次对区间染色为i,共染色N次。给出每次染色的区间,问最后能看见多少种颜色。

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

有一条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]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。

 

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

 

附:海报张数上限为10000,即其端点映射的新数轴长度最多为20000。因此建立长度为1QW的离散数组dis时,可以使用unsigned short类型,其映射值最多为20000,这样可以节约空间开销。

/******************
 * Author :fisty
 * data: 2014-10-26
 * poj2528
 * 线段树成段更新
 * ******************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;

#define MID(a,b) (a+((b-a)>>1))
const int N=20005;

struct node
{
        int lft,rht,co;
        int mid(){return MID(lft,rht);}
};

int a[N],b[N],n;
vector<int> y;
bool flag[N/2];
map<int,int> H;

struct Segtree
{
        node tree[N*4];
        void build(int l,int r,int k)
        {
                tree[k].lft=l;	
                tree[k].rht=r;
                tree[k].co=0;
                if(l + 1 != r)
                {
                        int mid=tree[k].mid();
                        build(l, mid, k << 1);
                        build(mid, r, k << 1 | 1);
                }
        }
        void updata(int st,int ed,int k,int co)
        {
                if(st <= tree[k].lft && tree[k].rht <= ed) tree[k].co=co;
                else 
                {
                        if(tree[k].co)
                        {
                                tree[k << 1].co=tree[k].co;
                                tree[k << 1|1].co=tree[k].co;
                                tree[k].co=0;
                        }
                        int mid=tree[k].mid();
                        //注意,这里与单点更新时不同。要分在左边,在右边,因为是区间所以要考虑跨区间情况
                        if(st < mid) 
                                updata(st, ed, k << 1,co);
                        if(ed > mid)
                                updata(st, ed, k << 1 | 1, co);
                }
        }
        void query(int st,int ed,int k)
        {
                int l=tree[k].lft,r=tree[k].rht;
                if(tree[k].co != 0 || l + 1 == r) 
                {
                        flag[tree[k].co]=1;
                        return;
                }
                query(st,ed,k << 1);	query(st,ed, k << 1 | 1);
        }
}seg;

int main()
{
        int t;
        scanf("%d",&t);
        while(t--)
        {
                y.clear(); H.clear();

                scanf("%d",&n);
                for(int i=0;i<n;i++) 
                {
                        flag[i]=0;
                        scanf("%d%d",&a[i],&b[i]);
                        b[i]++;
                        y.push_back(a[i]); y.push_back(b[i]);
                }

                sort(y.begin(),y.end());
                y.erase(unique(y.begin(),y.end()),y.end());
                for(int i = 0;i < (int)y.size(); i++) H[y[i]]=i;

                int len = (int)y.size();
                seg.build(0, len-1, 1);
                for(int i=0;i<n;i++) seg.updata(H[a[i]],H[b[i]],1,i+1);
                seg.query(1,len,1);
                int res=0;
                for(int i=1;i<=n;i++) if(flag[i]) res++;
                printf("%d\n",res);
        }
        return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值