POJ - 2528 - Mayor's posters (线段树区间覆盖+离散化)

题目:POJ - 2528

题意:n个人依次贴海报,给出每张海报所贴的范围li,ri 。求出最后还能看见多少张海报。

题解:因为范围过大,所以我们考虑离散化,离散化之后把数组的下标作为线段树建树的区间,但是要注意:

如下面的例子(题目的样例),因为单位1是一个单位长度,将下面的

      1   2   3   4  6   7   8   10

     —  —  —  —  —  —  —  —

      1   2   3   4  5   6   7   8

离散化  X[1] = 1; X[2] = 2; X[3] = 3; X[4] = 4; X[5] = 6; X[7] = 8; X[8] = 10

于是将一个很大的区间映射到一个较小的区间之中了,然后再对每一张海报依次更新在宽度为1~8的墙上(用线段树),最后统计不同颜色的段数。

但是只是这样简单的离散化是错误的,

如三张海报为:1~10 1~4 6~10

离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一张海报时:墙的1~4被染为1;
第二张海报时:墙的1~2被染为2,3~4仍为1;
第三张海报时:墙的3~4被染为3,1~2仍为2。
最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。

新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)

X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10

这样之后,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3

最终,1~2为2,3为1,4~5为3,于是输出正确结果3。

所以就需要每次在相差大于1的地方再加上一个数,这样才能保证答案的正确性,从而不让区间边界被修改时影响整体答案。

代码:

#include
#include
#include
#include
#define N 10005
using namespace std;
int t,n,ans,tot;
int a[4*N],li[N],ri[N],sum[N<<4];
bool vis[N<<4];
void pushdown(int node)
{
	if(sum[node]!=-1)
	{
		sum[node<<1]=sum[node];
		sum[node<<1|1]=sum[node];
		sum[node]=-1;
	}
}
void Update(int node,int x,int y,int l,int r,int z)
{
	if(x<=l&&r<=y)
	{
		sum[node]=z;
		return ;
	}
	pushdown(node);
	int m=(l+r)>>1;
	if(x<=m)Update(node<<1,x,y,l,m,z);
	if(y>m)Update(node<<1|1,x,y,m+1,r,z);
}
void Query(int node,int l,int r)
{
	if(sum[node]!=-1&&!vis[sum[node]])//这里只有pushdown操作,没有pushup
	{
		ans++;
		vis[sum[node]]=1;
		return ;
	}
	if(l==r)return ;
	pushdown(node);
	int m=(l+r)>>1;
	Query(node<<1,l,m);
	Query(node<<1|1,m+1,r);
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		tot=0;
		scanf("%d",&n);
		memset(sum,-1,sizeof(sum));
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&li[i],&ri[i]);
			a[++tot]=li[i];
			a[++tot]=ri[i];
		}
		sort(a+1,a+tot+1);
		int tmp=unique(a+1,a+tot+1)-(a+1);
		tot=tmp;
		for(int i=1;i<=tmp;i++)
			if(a[i]-a[i-1]>1)
				a[++tot]=a[i-1]+1;
		sort(a+1,a+tot+1);
		for(int i=1;i<=n;i++)
		{
			int x=lower_bound(a+1,a+tot+1,li[i])-a;
			int y=lower_bound(a+1,a+tot+1,ri[i])-a;
			Update(1,x,y,1,tot,i);//实际上是以下标为左右区间建树,而他们的值只是一个判断的标准,这样来实现
		}
		memset(vis,0,sizeof(vis));
		ans=0;
		Query(1,1,tot);
		printf("%d\n",ans);
	}
	return 0;
}

 

你可能感兴趣的:(ACM_刷题,ACM_线段树)