知识点:线段树(区间染色)
思路:
这个墙的长度很大,长达10000000,并且每组数据不会预先告诉你海报最远会铺到哪里,这样的话如果从头到尾整个墙进行建树等操作浪费的时间与空间都很大
可以对点进行离散化
———————————————————————————————————————
什么是离散化
离散化,就是把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。
什么时候用
当所给数据我们只需表示出它们之间的相对大小关系,而不需表示出具体数值时,就可以离散化。
const int N=1e5+7;
int t[N],a[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],t[i]=a[i];
sort(t+1,t+n+1);
m=unique(t+1,t+n+1)-(t+1); 返回值是去重之后的尾地址,减去开头才能是不重复的元素。
for(int i=1;i<=n;i++)
a[i]=lower_bound(t+1,t+m+1,a[i])-t;(再用a数组来储存其所在位置)
}
unique函数可以删除有序数组中的重复元素。(把邻近的重复元素消去)
注意:
a 这里的删除不是真的delete,而是将重复的元素放到容器末尾
b unique函数的返回值是去重之后的尾地址
c 一定要先对数组进行排序才可以使用unique函数
迭代器指向的地址也是迭代器指向的地址。
———————————————————————————————————————
大致的处理方法是,维护懒标记,但是向上维护的过程必须改成左右儿子(组成的两个区间)相同颜色赋值为相应的数字,如果为不同的颜色则赋值为0,到最后如果依然是0表示要从该节点开始统计。
如果在往下面维护的过程中发现大区颜色为0,就可以跳过,否则下面的都是大区的颜色,贴海报就直接把之前的最上层的tag覆盖就好。
然后的话,中途找一找端点最右端能到哪,以方便建树
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int c,n;
struct lktr{
int tr[50000000];
bool f[10000500];
void push_down(int r,int lf,int rg){
if(tr[r]==0)return ;(如果是0的话就没有往下传导的必要了)
tr[2*r]=tr[r];(如果大区是一个颜色两个小区肯定也是同一个颜色)
tr[2*r+1]=tr[r];
tr[r]=0;
}
void push_up(int r,int lf_r,int rg_r){
if(tr[lf_r]==tr[rg_r])(如果两者相同才赋值为当前颜色,否则的话是0,0代表混合颜色)
tr[r]=tr[lf_r];
else tr[r]=0;
}
void bud_tr(int r,int lf,int rg){
if(lf==rg){
tr[r]=0;
return ;
}
int lf_r=2*r;
int rg_r=2*r+1;
int mid=(lf+rg)/2;
bud_tr(lf_r,lf,mid);
bud_tr(rg_r,mid+1,rg);
tr[r]=0;
}
void update(int r,int srt,int edi,int lf,int rg,int k){
if(lf<=srt&&edi<=rg){(当前区间赋值)
tr[r]=k;
return ;
}
int lf_r=2*r;
int rg_r=2*r+1;
int mid=(srt+edi)/2;
push_down(r,srt,edi);
if(lf<=mid)update(lf_r,srt,mid,lf,rg,k);
if(mid<rg)update(rg_r,mid+1,edi,lf,rg,k);
push_up(r,lf_r,rg_r);
}
void query(int r,int lf,int rg){
if(tr[r]){(标记一下当前的颜色)
f[tr[r]]=1;
return ;
}
int lf_r=2*r;
int rg_r=2*r+1;
int mid=(lf+rg)/2;
push_down(r,lf,rg);(向下染色,后面的区间如果是0的话下面两个递归自然会碰到)
if(lf<=mid)query(lf_r,lf,mid);
if(mid<rg)query(rg_r,mid+1,rg);
}
}t;
struct pel{
int l;
int r;
}tem[10050],num[10050];
int temp[10050*5];
int main(){
scanf("%d",&c);
int i,j,a,b,tot=0;
for(i=1;i<=c;i++){
tot=0;
scanf("%d",&n);
memset(t.f,0,sizeof(t.f));(初始化)
memset(temp,0,sizeof(temp));
memset(tem,0,sizeof(tem));
memset(num,0,sizeof(num));
for(j=1;j<=n;j++){(对点进行离散化处理)
scanf("%d%d",&tem[j].l,&tem[j].r);
temp[++tot]=tem[j].l;
temp[++tot]=tem[j].r;
}
sort(temp+1,temp+tot+1);
tot=unique(temp+1,temp+tot+1)-(temp+1);()统计一下不重复的有多少
for(j=1;j<=n;j++){
num[j].l=lower_bound(temp+1,temp+tot+1,tem[j].l)-temp;(要减去最开头的位置,因为返回来的是地址)
num[j].r=lower_bound(temp+1,temp+tot+1,tem[j].r)-temp;
}
t.bud_tr(1,1,tot); (剩下的都是常规操作)
for(j=1;j<=n;j++)
t.update(1,1,tot,num[j].l,num[j].r,j);
t.query(1,1,tot);
tot=0;
for(j=1;j<=n;j++)
if(t.f[j])tot++;(统计一下颜色)
printf("%d\n",tot);
}
}