离散化是为了优化线段树的空间。
当遇到空间需求非常大时,可以考虑用离散化,这样能大大压缩空间。
例题:(来源 poj 2528)
【题目描述】有L(L没有给出)段线段(编号为1~L) 1~1000 0000(数组无法定义几千万,所以要用离散化),有 n次操作(1 <= n<= 1 0000),每次操作都是给出l和r,第i次操作的意义就是:把第l段到第r段的线段染色为颜色i。
【输入格式】第一行给出T,表示有T组数据。每组数据给出n表示下来有n个操作。下来n个操作,每行两个整数(l,r)表示把第l段到第r段的线段染色为颜色i。注意后面的染的颜色会覆盖前面的染的颜色。
【输出格式】问最后可以看到的有多少种不一样的颜色。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int len;
bool v[10010];
struct node1
{
int l,r,lc,rc,c;//c=-1时,表示它的范围里海报种类不一;\
c=0 时,表示它的范围里没有海报;\
c>0 时,表示它的范围里海报种类为c。
}tr[80010];//80010 > 2*40000 > 2*b[2*n].z
struct node2//离散化用结构体
{
int x,p,z;//x表示原来的值,\
z表示离散化后的值,\
p表示x在原来的位置。
}a[20010],b[20010],tmp;</span><pre name="code" class="cpp">
void erfen(int l,int r)//以b[i].x为排序依据,将它从小到大排序
{
int x=l,y=r,m=b[(l+r)/2].x;
while(x<=y)
{
while(b[x].x<m) x++;
while(b[y].x>m) y--;
if(x<=y)
{
tmp=b[x];
b[x]=b[y];
b[y]=tmp;
x++;y--;
}
}
if(l<y) erfen(l,y);
if(x<r) erfen(x,r);
}
void bt(int l,int r)
{
len++; int now=len;
tr[now].l=l;tr[now].r=r;tr[now].lc=tr[now].rc=-1;tr[now].c=0;
if(l<r)
{
int mid=(l+r)/2;
tr[now].lc=len+1; bt(l,mid);
tr[now].rc=len+1; bt(mid+1,r);
}
}
void change(int now,int l,int r,int k)
{
//if(tr[now].c==k) return ;
if(tr[now].l==l&&tr[now].r==r)
{
tr[now].c=k;
return ;
}
int mid=(tr[now].l+tr[now].r)/2;
int lc=tr[now].lc,rc=tr[now].rc;
if(tr[now].c>0) tr[lc].c=tr[rc].c=tr[now].c;
if(r<=mid) change(lc,l,r,k);
else if(mid+1<=l) change(rc,l,r,k);
else
{
change(lc,l,mid,k);
change(rc,mid+1,r,k);
}
if(tr[lc].c==tr[rc].c&&tr[lc].c>0) tr[now].c=tr[lc].c;
else tr[now].c=-1;
}
void wen(int now,int l,int r)
{
if(tr[now].c>=0)
{
v[tr[now].c]=true;
return ;
}
int mid=(tr[now].l+tr[now].r)/2;
int lc=tr[now].lc,rc=tr[now].rc;
if(tr[now].c>0) tr[lc].c=tr[rc].c=tr[now].c;
if(r<=mid) wen(lc,l,r);
else if(mid+1<=l) wen(rc,l,r);
else
{
wen(lc,l,mid);
wen(rc,mid+1,r);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[2*i-1].x,&a[2*i].x);
a[2*i-1].p=2*i-1;
a[2*i].p=2*i;
}
//离散化
for(int i=1;i<=2*n;i++) b[i]=a[i];
erfen(1,2*n);
b[1].z=1;//分配离散值
for(int i=2;i<=2*n;i++)
{
if(b[i].x==b[i-1].x) b[i].z=b[i-1].z;//相同值的离散值相同
else if(b[i].x-b[i-1].x>1) b[i].z=b[i-1].z+2;//难点。保留段与段之间的空位
else b[i].z=b[i-1].z+1;
}
for(int i=1;i<=2*n;i++) a[b[i].p].z=b[i].z;//将离散值转给对应的a
for(int i=1;i<=2*n;i++) a[b[i].p].z=b[i].z;//将离散值转给对应的a
//线段树
len=0; bt(1,b[2*n].z);
for(int i=1;i<=n;i++) change(1,a[2*i-1].z,a[2*i].z,i);
memset(v,false,sizeof(v));
wen(1,1,b[2*n].z);
int ans=0;
for(int i=1;i<=n;i++)
{
if(v[i]==true) ans++;
}
printf("%d\n",ans);
}
return 0;
}
推荐:《线段树—初始化与更改、查询》http://blog.csdn.net/a_bright_ch/article/details/54410757
《线段树—update延缓赋值优化》http://blog.csdn.net/a_bright_ch/article/details/54410785
《线段树—倒推优化》http://blog.csdn.net/a_bright_ch/article/details/54617401