poj.org/problem?id=2528
本文章除代码部分参考:優YoU http://blog.csdn.net/lyy289065406/article/details/6799170
大致题意:
有一面墙,被等分为1QW份,一份的宽度为一个单位宽度。现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1QW。后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报。现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?(PS:看见一部分也算看到。)
解题思路:首先建立模型:
给定一条数轴,长度为1QW,然后在数轴上的某些区间染色,第i次对区间染色为i,共染色N次。给出每次染色的区间,问最后能看见多少种颜色。
若第i次在区间[ai , bi]染色,则把[ai , bi]的每一格都染色为i。后染的颜色覆盖先染的颜色。由于染色N次,定义一个标记数组tagcol,从数轴第一格开始检查,一直检查到最后,出现过得颜色则记录到tagcol,最后统计tagcol中不同颜色的个数,就是所求。但是由于数据规模太大,必定TLE。因此使用线段树求解。
线段树的精髓是,能不往下搜索,就不要往下搜索,尽可能利用子树的根的信息去获取整棵子树的信息。如果在插入线段或检索特征值时,每次都非要搜索到叶子,还不如直接建一棵普通树更来得方便。
但是这题单纯用线段树去求解一样不会AC,原因是建立一棵[1,1QW]的线段树,其根系是非常庞大的,TLE和MLE是铁定的了。所以必须离散化。
通俗点说,离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变。举个例子:
有一条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]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。离散化时有一点必须要注意的,就是必须先剔除相同端点后再排序,这样可以减少参与排序元素的个数,节省时间。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX = 20010;
int n,ans,vis[MAX << 2],map[MAX << 2][2];
struct segtree{
int l,r,c;
int mid(){
return (l + r) >> 1;
}
}node[MAX << 2];
struct Line{
int p,mark;
}line[MAX << 2];
bool cmp(const Line &A, const Line &B){
return A.p < B.p;
}
void pushdown(int rt){//更新lazy操作
if(node[rt].c){
node[rt << 1].c = node[rt] .c;
node[rt << 1 | 1].c = node[rt].c;
node[rt].c = 0;
}
}
void buildtree(int l, int r, int rt){
node[rt].l = l;
node[rt].r = r;
node[rt].c = 0;
if(l == r) return;
int mid = node[rt].mid();
buildtree(l, mid, rt << 1);
buildtree(mid+1, r, rt << 1 | 1);
}
void insert(int l, int r, int rt, int color){//更新的是区间 因此要使用lazy标记 来维护线段树的时间复杂度
if(node[rt].l >= l && node[rt].r <= r){
node[rt].c = color;
return;
}
pushdown(rt);
int mid = node[rt].mid();
if(r <= mid) insert(l, r, rt << 1, color);
else if(l > mid) insert(l, r, rt << 1 | 1, color);
else insert(l, mid, rt << 1, color), insert(mid+1, r, rt << 1 | 1, color);
}
void update(int rt){
if(node[rt].c){
if(!vis[node[rt].c]){
ans++;
vis[node[rt].c] = 1;
}
return;
}
update(rt << 1);
update(rt << 1 | 1);
return;
}
int main(){
int cas;
scanf("%d",&cas);
while(cas--){
scanf("%d",&n);
for(int i=0; i<n; i++){
scanf("%d%d",&map[i][0],&map[i][1]);
line[i << 1].p = map[i][0], line[i << 1].mark = -(i+1);//记录线段的端点
line[i << 1 | 1].p = map[i][1], line[i << 1 | 1].mark = (i+1);
}
sort(line, line + (n << 1), cmp);//离散化的排序
int tmp = line[0].p, cnt = 1;
for(int i=0; i<(n << 1); i++){
if(tmp != line[i].p){//保证互异
cnt++;
tmp = line[i].p;
}
if(line[i].mark < 0)
map[-line[i].mark - 1][0] = cnt;
else
map[ line[i].mark - 1][1] = cnt;
}
buildtree(1, cnt, 1);
for(int i=0; i<n; i++)
insert(map[i][0], map[i][1], 1, i + 1);//插入时采用离散后的区间
memset(vis, 0, sizeof(vis));
ans = 0;
update(1);
printf("%d\n",ans);
}
return 0;
}