POJ 2528 & UVA 10587(线段树+离散+区间修改)

题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报。

思路:线段树直接做会TLE+MLE,因此需要离散。所谓离散就是将区段进行压缩,但是又不改变区间的位置关系。

方法就是将区段的端点值去掉相同的进行排序,举个例子:

给定4个区间[2,4] [3,6] [8,10] [6,9],覆盖关系就是后者覆盖前者,每个区间染色依次为 1 2 3 4。

现在我们抽取这4个区间的8个端点,2 4 3 6 8 10 6 9

然后删除相同的端点,这里相同的端点为6,则剩下2 4 3 6 8 10 9

对其升序排序,得2 3 4 6 8 9 10

然后建立映射

2     3     4     6     8     9   10

↓     ↓      ↓     ↓     ↓     ↓     ↓

1     2     3     4     5     6     7

那么新的4个区间为 [1,3] [2,4] [5,7] [4,6],覆盖关系没有被改变。新数轴为1到7,即原数轴的长度从9压缩到6,显然构造[1,7]的线段树比构造[1,10]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。

而这题的难点在于每个数字其实表示的是一个单位长度(并非一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
例子一:1-10 1-4 5-10
例子二:1-10 1-4 6-10
普通离散化后都变成了[1,4][1,2][3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define M ((L+R)>>1)
#define ls (o<<1)
#define rs (o<<1|1)
#define lson ls,L,M
#define rson rs,M+1,R
#define MAXN 10005
using namespace std;

int Ft[MAXN*2],F[MAXN*3],setv[MAXN*15];//F[]作为最终映射 Ft[]作为临时映射需要处理后放入F[]
bool vis[MAXN];
int Li[MAXN],Ri[MAXN],n,T,fn;
int yl,yr,ans,v;

void pushdown(int o,int L,int R) {if(setv[o]>=0) {setv[ls]=setv[rs]=setv[o];setv[o]=-1;}}
void update(int o,int L,int R)
{
	if(yl<=L && R<=yr) {setv[o]=v;return;}
	pushdown(o,L,R);
	if(yl<=M) update(lson);
	if(M<yr) update(rson);
}
void query(int o,int L,int R)
{
	if(setv[o]>=0)
	{
		if(!vis[setv[o]]) vis[setv[o]]=1,++ans;
		return;
	}
	if(L==R) return;
	if(1<=M) query(lson);
	if(M<fn-1) query(rson);
}
int main()
{
	scanf("%d",&T);
	while(T-- && scanf("%d",&n))
	{
		int nn=0;
		for(int i=0;i<n;++i) {scanf("%d%d",&Li[i],&Ri[i]);Ft[nn++]=Li[i];Ft[nn++]=Ri[i];}
		sort(Ft,Ft+nn);
		F[1]=Ft[0],fn=2;
		for(int i=1;i<nn;++i)
		if(Ft[i]!=Ft[i-1])
		{
			if(Ft[i]-Ft[i-1]>1) F[fn++]=Ft[i-1]+1;
			F[fn++]=Ft[i];
		}
		memset(vis,0,sizeof(vis));
		memset(setv,-1,sizeof(setv));
		for(v=0;v<n;++v)
		{
			yl=lower_bound(F+1,F+fn,Li[v])-F,yr=lower_bound(F+1,F+fn,Ri[v])-F;
			update(1,1,fn-1);
		}
		ans=0;
		query(1,1,fn-1);
		printf("%d\n",ans);
	}
	return 0;
}

你可能感兴趣的:(哈希,线段树,离散,区间修改)