1、经典的离散化题目,用线段树加速。
2、离散化就是将无限空间(或很大空间)的点映射到有限区域(或很小区域),说得通俗点就是“只保存我们需要的一些点”,一般指区间端点。具体见注释。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
const int MAX=20010;
using namespace std;
int c,n,ls[MAX];
struct node{
int l,r;
int c;
}tree[MAX*4];
struct ln{
int li,num;
}line[MAX];
int set[MAX][2];
bool vis[MAX];
int ans,tp;
bool cmp(struct ln a,struct ln b){
return a.li<b.li;
}
void Inittree(int pos,int ll,int rr){
tree[pos].l=ll;
tree[pos].r=rr;
tree[pos].c=0;//将着色数清零
if(ll!=rr){
int mid=(ll+rr)/2;
Inittree(pos*2,ll,mid);
Inittree(pos*2+1,mid+1,rr);
}
}
void Insert(int pos,int ll,int rr,int color){
if(tree[pos].l>=ll&&tree[pos].r<=rr){//若所要修改的区间覆盖该线段,直接修改返回即可。
tree[pos].c=color;
return;
}
if(tree[pos].c>0&&tree[pos].c!=color){ //如果已经着了不同颜色,将lazy标记向下传,自己颜色标记为0。
tree[pos*2].c=tree[pos].c;
tree[pos*2+1].c=tree[pos].c;
tree[pos].c=0;
}
int mid=(tree[pos].l+tree[pos].r)/2;
if(rr<=mid){
Insert(pos*2,ll,rr,color);//在左边覆盖
}else if(ll>mid){
Insert(pos*2+1,ll,rr,color);//在右边覆盖
}else{
Insert(pos*2,ll,mid,color);//如果横跨中点,在两边覆盖
Insert(pos*2+1,mid+1,rr,color);
}
}
void Search(int pos){
if(pos>=4*tp) return;//超界了直接返回
if(tree[pos].c!=0){
if(!vis[tree[pos].c]){//若有颜色(单色)且未访问,色数加一,返回
vis[tree[pos].c]=true;
ans++;
}
return;
}
Search(2*pos);//若是混合色,访问两边
Search(2*pos+1);
}
int main(){
scanf("%d",&c);
while(c--){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d%d",&set[i][0],&set[i][1]);//读入初始的区间端点,等下还要调整(等下保存编号)
line[2*i].li=set[i][0];//保存真实坐标
line[2*i].num=-(i+1);//其绝对值为所在区间编号,若是区间左端点为负,右端点为正
line[2*i+1].li=set[i][1];
line[2*i+1].num=i+1;
}
sort(line,line+2*n,cmp);//由于数据是任意顺序输入的,所以要按照真实坐标排成不降序
int temp=line[0].li;
tp=1;
for(int i=0;i<2*n;i++){
if(line[i].li!=temp){
tp++;
temp=line[i].li;
}//去重,tp保存不同的坐标数
if(line[i].num<0){
set[-line[i].num-1][0]=tp;//赋予新编号,也就是映射后的下标
}
else{
set[line[i].num-1][1]=tp;
}
}
Inittree(1,1,tp);//初始化线段树
for(int i=0;i<n;i++)
Insert(1,set[i][0],set[i][1],i+1);//修改线段颜色,四个参数分别是:初始树结点编号,左端点映射值,右端点映射值,颜色编号
memset(vis,0,sizeof(vis));
ans=0;
Search(1);
printf("%d\n",ans);
}
return 0;
}