刚接触离散化,开始对其百思不得其解,现在回想,顿时开窍,对线段树的离散化才有了初步了解。于是打算写上这么一篇博文,分享自己的理解过程。注:文中的代码并非完全由我原创,是我根据自己的理解对其进行了些修改,如有错误,望指正。
题目链接:点击打开链接
题目大意:
在一面墙上以一定的先后顺序贴上海报,每张海报有对应的起始及终止区间,求当最后一张海报贴完后,墙上没有被完全遮盖的海报张数。
题目分析:
个人觉得这个题目比较经典,因为它不但包括了线段树的基本框架,还融入了延迟更新与离散化,考察方面较广。
我们知道线段树在解决区间问题上是比较适用的,而这题的每张海报其实就是一个区间,所以此题用线段树求解是比较方便的。然而这个题的特殊之处是它数据范围(海报的左右端点1<=li<=ri<=10^7)所以直接开一个这么大的线段树数组不但很可能会超内存,在查询时还一定会超时。所以这里需要运用到离散化的思想,将每个端点的值分别数1-n建立映射关系,以适当减小两端点的间隔差。
但是进行普通的映射是存在问题的。如:当三张海报的区间分别是[1-10],[1-4],[7-10]时,在对其端点进行排序后,若建立的映射为:
y: 1 4 7 10
x: 1 2 3 4
则当输入[1-10]时线段树中对应的区间[1-4]被标记为0(即它是数组中的第一个数据);输入区间[1-4]时,对应的区间[1-2]被标记为1;输入区间[7-10]时,对应的区间[3-4]。这样一来根据映射,区间[1-10]就被[1-4]和[7-10]所覆盖,而实际上并没有。
为什么会这样呢?其实,是在进行上述映射时,原来数据(即海报)的区间间隔差被过度减小了。所以为了不改变原有数据间隔的相对性质,我们在间隔差大于1的两相邻数据间再添加一个数(这个数不对应任何数据,只起到分离两相邻区间的作用),则原来的映射关系就变为:
y: 1 4 7 10
x: 1 2 3 4 5 6 7
我们这样做其实是将原来两相邻区间间隔差大于2的缩小至2,而非为1。如原来[1-4],[7-10],对应的区间[1-2],[2-3]的间隔差为3-2=1,而修改后的为5-3=2。
下面是代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10010;
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
bool judge[maxn];
struct Post
{
int li;
int ri;
}post[maxn];
int Mp[maxn<<3]; //开辟用于映射的数组
int tree[maxn<<4];
int cnt;
void pushdown(int rt)
{
if(tree[rt]!=-1)
{
tree[rt<<1] = tree[rt];
tree[rt<<1|1] = tree[rt];
tree[rt] = -1;
}
}
void updata(int L, int R, int val, int l, int r, int rt)
{
if(L<=l && r<=R)
{
tree[rt] = val;
return ;
}
pushdown(rt);
int mid = (l+r)>>1;
if(L<=mid)
updata(L, R, val, lson);
if(R>mid)
updata(L, R, val, rson);
}
void query(int l, int r, int rt)
{
if(tree[rt]!=-1)
{
if(!judge[tree[rt]])
cnt++;
judge[tree[rt]] = true;
return;
}
if(l == r) //考虑到li == ri情况
return ;
int mid = (l+r)>>1;
query(lson);
query(rson);
}
//将映射中每张海报的区间端点与海报张贴的顺序匹配
int find(int val, int n)
{
int l = 0;
int r = n;
while(l<=r)
{
int mid = (l+r)>>1;
if(Mp[mid]==val)
return mid;
if(Mp[mid]>val)
r = mid-1;
else
l = mid+1;
}
return -1;
}
//建立映射关系
int Mapping(int nn)
{
sort(Mp, Mp+nn);
int m=1, i;
for(i=1; i<nn; i++)
{
if(Mp[i]!=Mp[i-1])
Mp[m++] = Mp[i];
}
for(i = m-1; i>0; i--)
{
if(Mp[i]>Mp[i-1]+1)
Mp[m++] = Mp[i-1] +1;
}
sort(Mp, Mp+m);
return m;
}
int main()
{
int c, n;
scanf("%d", &c);
while(c--)
{
scanf("%d", &n);
int i, nn=0;
for(i=0; i<n; i++)
{
scanf("%d%d", &post[i].li, &post[i].ri);
Mp[nn++]= post[i].li;
Mp[nn++]=post[i].ri;
}
int m = Mapping(nn);
memset(tree, -1, sizeof(tree));
for(i=0; i<n; i++)
{
int l = find(post[i].li, m-1);
int r = find(post[i].ri, m-1);
updata(l, r, i, 0, m-1, 1);
}
memset(judge, false, sizeof(judge));
cnt = 0;
query(0, m-1, 1);
printf("%d\n", cnt);
}
return 0;
}