线段树的离散化,因为贴海报的范围是1~1e7,肯定开不了那么大的数组,但是n的范围很小只有1e4,所以可以离散化处理,什么叫做离散化?
百度百科:离散化,把无限空间中无限的个体映射到有限的空间中去,以此提高算法的时空效率。
比如一组数据:
3
10000 100000
10000 20000
20000 100000
心算就能算出来应该是3,我们可以把10000假设成1,100000假设成10,20000假设成2,这样就成了
3
1 10
1 2
2 10
其实还可以继续缩小 因为10000最小排在第一,20000第二,100000第三,所以假设10000为1,20000为2,100000为3
即:
3
1 3
1 2
2 3
因此离散化就避免了很大的空间浪费,所以虽然说数的范围是1~1e7,但是只有2e4个数,我们通过映射,可以将这些数映射成1~20000的范围,这样就只需要开2w的数组即可,而不再需要开1e7的数组,是不是很神奇啊?哈哈~
不过应该怎么映射?
可以通过map映射,不过可能会超时,我们可以将所有的数sort一下,再去重,然后每个数都对应了一个次序,按照这个次序来建树,更新,最后再求范围就可以了,
poj:
poj AC代码(nyoj wa):
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
int num[20010],k,c[20010];
struct node
{
int left,right,value,mid;
} w[80020];
struct stu
{
int x,y;
} a[10010];
void build(int l,int r,int tr)
{
w[tr].value=-1;
w[tr].left=l,w[tr].right=r;
if(l==r) return ;
int mid=(l+r)/2;
w[tr].mid=mid;
build(l,mid,tr*2);
build(mid+1,r,tr*2+1);
}
int Search(int x)
{
int l=1,r=k,j;
while(l<=r)
{
j=(l+r)/2;
if(num[j]==x)
return j;
if(x<num[j])
r=j-1;
else
l=j+1;
}
}
void update(int l,int r,int zhi,int tr)
{
if(l==w[tr].left&&r==w[tr].right)
{
w[tr].value=zhi;
return ;
}
if(w[tr].value!=-1)
w[tr*2].value=w[tr*2+1].value=w[tr].value,w[tr].value=-1;
if(r<=w[tr].mid)
update(l,r,zhi,tr*2);
else if(l>w[tr].mid)
update(l,r,zhi,tr*2+1);
else
update(l,w[tr].mid,zhi,tr*2),update(w[tr].mid+1,r,zhi,tr*2+1);
}
void searchtree(int tr)
{
if(w[tr].value!=-1)
{c[w[tr].value]++;return;}
if(w[tr].left==w[tr].right) {return;}
searchtree(tr*2),searchtree(tr*2+1);
}
int main()
{
int nn;
scanf("%d",&nn);
while(nn--)
{
memset(c,0,sizeof(c));
int n,i;
k=0;
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%d%d",&a[i].x,&a[i].y),num[++k]=a[i].x,num[++k]=a[i].y;
sort(num+1,num+k+1);
k=unique(num+1,num+k+1)-num-1;
build(1,k,1);
for(i=1; i<=n; i++)
{
int x=Search(a[i].x);
int y=Search(a[i].y);
update(x,y,i,1);
}
searchtree(1);
int s=0;
for(i=1; i<=n; i++)
if(c[i])
s++;
printf("%d\n",s);
}
}
然后换个oj提交
这是会惊奇的发现wa了,为什么?
再来测一组数据
3
1 10
1 4
6 10
实际结果应该为3,但是代码的结果为2,为什么?
因为映射过程中成了:
3
1 4
1 2
3 4
不难理解为什么代码结果和实际为什么不一样,但是poj后台数据水,就a了,,幸亏我nyoj后台叼~,哈哈,,
改进代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<set> //set用来记录海报个数
#include<iostream>
using namespace std;
set<int>s; //set容器
int num[20010],k;
struct node
{
int left,right,value,mid;
} w[40020];
struct stu
{
int x,y;
} a[10010];
void build(int l,int r,int tr) //建树,但是不是一般的建树法
{
w[tr].value=-1;
w[tr].left=l,w[tr].right=r;
int mid=(l+r)/2;
w[tr].mid=mid;
if(l+1<r) //注意此处的建树法
{
build(l,mid,tr*2);
build(mid,r,tr*2+1);
}
}
int Search(int x) //二分查找用来查找这个数对应的是离散化的哪个数字,
{
int l=1,r=k,j;
while(l<=r)
{
j=(l+r)/2;
if(num[j]==x)
return j;
if(x<num[j])
r=j-1;
else
l=j+1;
}
}
void update(int l,int r,int zhi,int tr) //区间更新
{
if(l==w[tr].left&&r==w[tr].right) //若查找到此区间,则“贴上海报”
{
w[tr].value=zhi;
return ;
}
if(w[tr].value!=-1) //若此区间没有找到,且此处有海报,就将其分成小块,
w[tr*2].value=w[tr*2+1].value=w[tr].value,w[tr].value=-1;
if(r<=w[tr].mid)
update(l,r,zhi,tr*2);
else if(l>=w[tr].mid)
update(l,r,zhi,tr*2+1);
else
update(l,w[tr*2].right,zhi,tr*2),update(w[tr*2+1].left,r,zhi,tr*2+1);
}
void searchtree(int tr) //查询
{
if(w[tr].value!=-1)
s.insert(w[tr].value); //找到海报就放进set容器中,同时这个区间的子区间就不再统计,
else if(w[tr].right>w[tr].left+1)
{
searchtree(2*tr);
searchtree(2*tr+1);
}
}
int main()
{
int nn;
scanf("%d",&nn);
while(nn--)
{
int n,i;
k=0;
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%d%d",&a[i].x,&a[i].y),a[i].x--,num[++k]=a[i].x,num[++k]=a[i].y; //num数组用来排序,便于离散化,至于为什么要a[i]--,自己想想,哈哈~
sort(num+1,num+k+1); //num排序,
k=unique(num+1,num+k+1)-num-1; //用了unique函数,目的是去除排序后的重复数字,
build(1,k,1); //建树
for(i=1; i<=n; i++) //n次更新
{
int x=Search(a[i].x);
int y=Search(a[i].y);
update(x,y,i,1);
}
searchtree(1); //查询有多少个海报
printf("%d\n",s.size()); //输出海报个数
s.clear(); //清空set容器
}
return 0;
}