第一行: 样例个数T
第二行: 贴海报的人n
第三行: 每个人贴海报的范围
接下来n行: 每个人贴海报的范围
对于每一个输入,输出最后可以看到的海报张数。下面这个图是样例解释
1 5 1 4 2 6 8 10 3 4 7 10
4
刚学的线段树,一直以为线段树只是求区间的问题,套套模板就行了,可是这道题套不成,花了两天时间才写出来。首先线段树大多数解决的是1e4~1e6数据范围,可是这道题范围太大,暴力不是超时就是超内存,所以只能离散化处理。 怎么离散化才能够更加合理呢?因为本题的区间范围太大,不能用到线段树,所以我们可以在缩小区间上面做文章。比如:1 4, 5 10,这两个区间,我们可以让这四个数,一一对应一个数来代表它们,不用想肯定是下标最合适,所以1就对应1,2对应4,3对应5,4对应10,区间对应之后就是1 2,3 4; 这样离散化还没有结束,因为还有这种情况比如:1 10,1 4,6 10,按照刚刚那种对应关系,区间对应之后就,1 4,1 2,3 4,对应之后只能看到两种颜色,可是这种情况能看到三种颜色,因为4~6之间还有一种颜色,可是由于离散化给忽视了,解决的方法是我们可以在4~6之间再加一个区间5,这样的话5就代表了一种颜色,而不被忽视。具体的离散化请看下列代码分析:
//先将所有的端点存入坐标中进行排序
int t = 0;
scanf("%d", &N);
for(int i = 1; i <= N; i++) {
scanf("%d %d", &s[i].left, &s[i].right);
num[++t] = s[i].left;//下标从1开始
num[++t] = s[i].right;
}
sort(num+1, num+t+1);//从小到大排序
接下来处理被离散化而忽视的那种情况。排过序之后,如果每个端点相差大于1,那么就在这两个端点之间再加一个端点,可是如果两个端点相等就没有意义,因为离散化就是一一对应,没必要对重复的数进行一一对应,所以还需要一步去重,去过重之后,然后再进行上一步的离散化处理。
//去重处理
int m = unique(num+1, num+t+1)-num;
/*这里需要多注意,因为用到了下标,下标一一对应,所以下标不管是从
0开始,还是从1开始都要进行一一对应,不然就会导致离散化出错
*/
int p = m-1;
for(int i = 1; i < p; i++) {
if(num[i+1]-num[i] > 1)//因为已经按照升序排序,以及去重处理,所以num[i+1]必大于num[i]
num[m++] = num[i]+1;//在两者之间加上一个数
}
//处理完之后,再进行一步排序
sort(num+1, num+m);
这样离散化就好了,然后就是线段树的操作了,为了更快找到对应的端点,直接用二分查找就可以了。具体请看代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
struct node{
int left;
int right;
}s[maxn];
bool hash[maxn];
int num[maxn<<4];
int value[maxn<<4];
void Pushdown(int i) {//延迟标记
value[i<<1] = value[i<<1|1] = value[i];
value[i] = -1;
}
void Update(int numl, int numr, int C, int l, int r, int i) {
if(numl <= l && numr >= r) {//找到区间
value[i] = C;
return ;
}
if(value[i] != -1) Pushdown(i);//更新下个区间
int m = (l+r)>>1;
if(numr <= m) Update(numl, numr, C, l, m, i<<1);
else if(numl > m) Update(numl, numr, C, m+1, r, i<<1|1);
else {
Update(numl, m, C, l, m, i<<1);
Update(m+1, numr, C, m+1, r, i<<1|1);
}
return ;
}
int ans;
void Query(int i, int l, int r) {
if(value[i] != -1) {
if(hash[value[i]] == false) {
ans++;
hash[value[i]] = true;
}
return;
}
if(l == r) return ;
int m = (l+r)>>1;
Query(i<<1, l, m);
Query(i<<1|1, m+1, r);
}
int main() {
int T;
int N;
scanf("%d", &T);
while(T--) {
memset(value, -1, sizeof(value));
memset(hash, false, sizeof(hash));
int t = 0;
scanf("%d", &N);
for(int i = 1; i <= N; i++) {
scanf("%d %d", &s[i].left, &s[i].right);
num[++t] = s[i].left;//下标从1开始
num[++t] = s[i].right;
}
//处理一
sort(num+1, num+t+1);
//去重处理
int m = unique(num+1, num+t+1)-num;
int p = m-1;
//处理二
for(int i = 1; i < p; i++) {
if(num[i+1]-num[i] > 1)
num[m++] = num[i]+1;
}
sort(num+1, num+m);
for(int i = 1; i <= N; i++) {
int l = lower_bound(num+1, num+m, s[i].left)-num;//二分查找
int r = lower_bound(num+1, num+m, s[i].right)-num;
Update(l, r, i, 1, m-1, 1);//更新
}
ans = 0;
Query(1, 1, m-1);//查询
printf("%d\n", ans);
}
return 0;
}