// Creat By 郭仔 2012年3月29日10:16:59
区间染色的变形,不过比区间染色问题要难一些~
用到区间染色成段更新,hash,离散化,蛋疼的提
题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了
所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
而这题的难点在于每个数字其实表示的是一个单位长度(并且一个点),这样普通的离散化会造成许多错误poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
1-10 1-4 5-10
1-10 1-4 6-10
为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.
还是不知道为啥会出现这种情况
————————————————————————————————————————————————————————
假设给定的区间是[1,3],[6,1000],我们可以如下离散
离散前坐标:1 3 6 1000
离散后坐标:1 2 3 4
于是我们就减少了没必要的搜索过程和内存空间。有个建树时的小技巧,因为我建树的每个节点是开区间到闭区间,即[a,b)。于是在读入数据的时候我就可以把b的值加一,这样就很好的处理了题目中可能出现的[a,a]相等值的区间(也就是对一个点的处理)。
根据这一点可以更改下面的程序的cout,不用加1,而在输入值时区间右边界加1就行
一下来自http://chenjiapeng.blogbus.com/logs/72973396.html
现在做个小小的总结:
1.线段树不会有固定的模板,只是种思想,一般可能都需要离散化。
2.分成建树,染色,判断统计3个步骤,可能其他大致也就3个类似的步骤。
3.如果有单个点的情况,建区间时可以把右端点+1处理。
有很多还不是很清楚,等以后具体研究时再去发掘吧。
/*线段树*/
#include <iostream>
#include <algorithm>
using namespace std;
const int Max=20010;
struct point//记录n个区间
{
int l;
int r;
}a[Max];
struct Node
{
int l;
int r;
int mid;
int color;
}tree[Max*4];
int t,n;
int hash[Max*2];//哈希是种态度 = =
bool used[Max];//最后统计单颜色的区间
void make(int l,int r,int num)//构建线段树
{
tree[num].l = l;
tree[num].r = r;
tree[num].mid=(l+r)/2;
tree[num].color=0;
if(l+1!=r)//不是叶区间
{
make(l,tree[num].mid,num*2);
make(tree[num].mid,r,num*2+1);
return;
}
}
/*染色函数*/
void insert(int num,int l,int r,int c)//节点编号num. 要染色的区间
{ //的左右端点l,r和颜色c
if(tree[num].color!=c)//区间的颜色不是所要染得色c
{ //如果是c,说明已经上满了c
if(tree[num].l==l&&tree[num].r==r)//区间完全覆盖,给该区间染上c
{
tree[num].color=c;
return;
}
//以下是区间未被完全覆盖的情况
if(tree[num].color>=0)//区间未被上色或者之上一种色(这个颜色是上满的)
{ //那么其子节点 也是这个颜色
tree[2*num].color=tree[num].color;
tree[2*num+1].color=tree[num].color;
tree[num].color=-1;//由于一开始有颜色(或者没有)
//上色的c只占区间的一部分,所以是杂色
}
if(r<=tree[num].mid)//染色区间在该区间的左子区间
insert(2*num,l,r,c);
else if(l>=tree[num].mid)//染色区间在该区间的右子区间
insert(2*num+1,l,r,c);
else//染色区间横跨区间的左右区间
{
insert(num*2,l,tree[num].mid,c);
insert(num*2+1,tree[num].mid,r,c);
}
}
}
/*统计函数
统计最后的颜色区间*/
void count(int num)
{
if(tree[num].color>0)
{
used[tree[num].color]=true;
return ;
}
if(tree[num].l+1!=tree[num].r)
{
count(2*num);
count(2*num+1);
}
}
/*查找函数
val是未离散化前面的值。
函数返回的是其在离散化后的区间的编号*/
int b_search(int l,int r,int val)
{
while(l<=r)
{
int mid=(l+r)/2;
if(hash[mid]==val)
return mid;
else if(hash[mid]>val)
r=mid-1;
else
l=mid+1;
}
return -1;
}
int main()
{
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
scanf("%d",&n);
memset(used,false,sizeof(used));
for(int j=1;j<=n;j++)
{
scanf("%d %d",&a[j].l,&a[j].r);
++a[j].r;
hash[2*j-1]=a[j].l;
hash[2*j]=a[j].r;
}
sort(hash+1,hash+1+2*n);
int index=2;
for(int j=1;j<2*n;j++)
if(hash[j]!=hash[j+1])
hash[index++]=hash[j+1];
make(1,index-1,1);
for(int j=1;j<=n;j++)
{
int lset=b_search(1,index-1,a[j].l);
int rset=b_search(1,index-1,a[j].r);
insert(1,lset,rset,j);
}
count(1);
int ans=0;
for(int j=0;j<Max;j++)
if(used[j])
ans++;
cout<<ans<<endl;
}
return 0;
}