此题目的线段树就是一般的线段树思路,但是我们会发现他的范围实在是太大,怎么办?
- 离散化!
离散化的目的就是为了压缩空间,这个题目的离散化也是比较简单的:
例如样例有5个区间[1,4],[2,6],[8,10],[3,4],[7,10]
然后我们将这十个端点拿出来,分别是1 4 2 6 8 10 3 4 7 10
将这十个端点排序,删掉重复的点,即1 2 3 4 6 7 8 10
再进行映射
1 2 3 4 6 7 8 10
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
1 2 3 4 5 6 7 8
这样把每个端点都映射为相应的端点,压缩了空间,而且没有改变覆盖关系。改变后的端点为[1,4],[2,6],[8,10],[3,4],[7,10]
接下来就是线段树的建树,更新,查询,代码如下:
#include"cstdio"
#include"cstring"
#include"algorithm"
#define maxn 10002
#define MAX 10000010
using namespace std;
struct {
int l,r; //左右区间
int mid() {
return (l+r) >> 1;
}
int id; //颜色
int type; //懒惰标记
}post[4*maxn];
struct {
int st,en;
}ss[maxn];
int hash[MAX],lis[2*maxn],ans;
bool vis[maxn];
void Build(int l,int r,int no) {
post[no].l = l;post[no].r = r;post[no].id = 0;post[no].type = 0;
if (l == r) return;
Build(l,post[no].mid(),no<<1);
Build(post[no].mid()+1,r,no<<1|1);
}
void Updata(int l,int r,int no,int id) {
if (l <= post[no].l && post[no].r <= r) {
post[no].id = id;
post[no].type = id;
return;
}
//if (post[no].l == post[no].r) return;
if (post[no].type) { /*下放一次*/
post[no<<1].type = post[no<<1|1].type = post[no].type;
post[no<<1].id = post[no<<1|1].id = post[no].id;
post[no].type = 0;
}
int mid = post[no].mid();
if (l <= mid) Updata(l,r,no<<1,id);
if (r > mid) Updata(l,r,no<<1|1,id);
if (post[no<<1].id == post[no<<1|1].id)
post[no].id = post[no<<1].id; else post[no].id = 0;
}
void Query(int l,int r,int no) {
if(post[no].l < l || post[no].r > r) return;//防止编号出界
if(post[no].id) {
if (!vis[post[no].id]) {
ans ++;
vis[post[no].id] = 1;
}
return;
}
Query(l,r,no<<1);
Query(l,r,no<<1|1);
}
int main() {
int T;
scanf("%d",&T);
while (T --) {
memset(hash,0,sizeof(hash));//垃圾的哈希表
memset(vis,0,sizeof(vis));
int n,k = 0;
scanf("%d",&n);
for (int i = 0;i < n;i ++) {
scanf("%d%d",&ss[i].st,&ss[i].en);
lis[k ++] = ss[i].st;
lis[k ++] = ss[i].en;
}
sort(lis,lis+k);
int j = 0;
for(int i = 0;i < k;i ++)
if(!hash[lis[i]]) hash[lis[i]] = ++j;
Build(1,j,1);
for (int i = 0;i < n;i ++)
Updata(hash[ss[i].st],hash[ss[i].en],1,i+1);
ans = 0;
Query(1,j,1);
printf("%d\n",ans);
}
return 0;
}