题目链接
对于这个题,网上的很多题解对离散化的过程基本是一笔带过,本憨憨看了很久才懂。现在在这详细讲一下这个题区间的离散化。
首先,看下面这个例子(几个区间):
[1, 5]
[2, 7]
[8, 10]
在数轴上表示:
这是咱们发现一个事实:对于这些区间,我们保留某些点的信息是没有意义的,比如:3,4,6,9。对于这些点,它们一直被包含在一个大区间里。所以,我们考虑把这些点压缩点,你可以想象,把一个数组那些非端点给切掉,然后把剩下的部分拼接起来,然后把端点从1开始编号,就形成了:
不难发现,这个被压缩的数轴在这个题中与原数轴是等价的。这时,我们便完成了离散化的过程,(从10的长度压缩到了6)将一些没有必要记录信息的非端点压缩掉了。那么,我们应该怎么完成这个过程呢:
对于这个例子,我们先把三个区间读入,然后把所有端点存在一个数组中,排序,去重:
1 2 5 7 8 9
这里注意的是:一个端可能在不同的区间中出现多次。而每个端点咱们存一次就好了。(判重)
那么,他在数组中的序号就是新端点的编号。
而对于这个题来将,传统离散有一个问题:
例如:
[1,10]
[1, 4]
[6,10]
答案应该是3,但是,如果按上述方法,答案会是2,原因:在压缩时,我们将仍然漏在外面的5给压缩掉了,所以我们在所有相隔距离大于1的点之间都插入一个单点(让它充当那段被裸露区间),然后此问题得以解决。
至此本题的离散化过程完成,下面的线段树就是模板了。阐述的题解有很多,这里一笔带过。
ps. 最后说一下两个stl函数吧,
unique(begin, end),对于一个容器、数组,自动判重(相邻位置),返回末地址。
lower_bound(begin,end,a)二分找非递减序列中第一个大于等于a的元素地址。
下面是ac代码(模板来自蓝皮书):
#include <iostream>
#include <string>
#include <cstring>
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 200005;
struct Node
{
int l, r;
int gg;
}tr[(N<<2)+5];
int lq[N], rq[N];
int lisan[N];
int cnt, ans;
bool vis[N];
void build(int p, int l, int r)
{
tr[p].l = l; tr[p].r = r;
if (l == r){tr[p].gg = 0; return;}
int mid = (l + r) >> 1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
tr[p].gg = 0;
}
void spread(int q)
{
if (tr[q].gg == 0) return;
tr[q<<1].gg = tr[q].gg;
tr[q<<1|1].gg = tr[q].gg;
tr[q].gg = 0;
}
void update(int q, int l, int r, int v)
{
if (l <= tr[q].l && r >= tr[q].r)
{tr[q].gg = v; return;}
spread(q);
int mid = (tr[q].l + tr[q].r) >> 1;
if (l <= mid) update(q<<1, l, r, v);
if (r > mid) update(q<<1|1, l, r, v);
}
void ask(int q, int l, int r)
{
if (tr[q].gg && !vis[tr[q].gg])
{
ans++;
vis[tr[q].gg] = 1;
return;
}
if (l == r) return;
spread(q);
int mid = (l + r) >> 1;
ask(q<<1, l, mid);
ask(q<<1|1, mid+1, r);
}
int main()
{
int t;
cin >> t;
while(t--)
{
int n;
cnt = 0;
memset(vis, 0, sizeof(vis));
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d%d", &lq[i], &rq[i]);
lisan[cnt++] = lq[i];
lisan[cnt++] = rq[i];
}
sort(lisan, lisan + cnt);
int m = unique(lisan, lisan+cnt) - lisan;
int t0 = m;
for (int i = 1; i < m; i++)
if(lisan[i] - lisan[i-1] > 1) lisan[t0++] = lisan[i-1] + 1;
sort(lisan, lisan+t0);
build(1, 1, t0);
for (int i = 0; i < n; i++)
{
int x = lower_bound(lisan, lisan+t0, lq[i]) - lisan + 1;
int y = lower_bound(lisan, lisan+t0, rq[i]) - lisan + 1;
// cout <<x << " " << y << endl;
update(1, x, y, i + 1);
}
ans = 0;
ask(1, 1, t0);
printf("%d\n", ans);
}
}