poj解题报告——2528

        题意:有一面墙,被等分为1千万份,一份的宽度为一个单位宽度。现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1千万。后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报。现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?看见一部分也算看到。

        解法:线段树+离散化

        有一条1到10的数轴(长度为9),给定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]的线段树更省空间,搜索也更快,且结果一致。

代码如下

#include<stdio.h>
struct node
{
	int aa,bb,left,right,flag,color;
}c[100000];
int b[50000],a[50000][3],f[50000],tot;
void build(int x,int y)
{
	int now;
	tot++;
	now=tot;
	c[now].aa=x;
	c[now].bb=y;
	c[now].flag=0;
	c[now].color=0;
	if(x!=y)
	{
		c[now].left=tot+1;
		build(x,(x+y)/2);
		c[now].right=tot+1;
		build((x+y)/2+1,y);
	}
}
void insert(int x,int y,int z,int xg)
{
	int mid;
	if(x<=b[c[z].aa]&&y>=b[c[z].bb])
	{
		c[z].flag=1;
		c[z].color=xg;
	}
	else
	{
		if (c[z].flag==1)
		{
			c[c[z].right].color=c[c[z].left].color=c[z].color;
			c[c[z].left].flag=c[c[z].right].flag=1;
			c[z].flag=0;
		}
	mid=(c[z].aa+c[z].bb)/2;
	if(x<=b[mid])
		insert(x,y,c[z].left,xg);
	if(y>b[mid]) 
		insert(x,y,c[z].right,xg);
	}
}
void find(int x)
{
	if(c[x].flag==1)
		f[c[x].color]=1;
	else if(c[x].aa!=c[x].bb)
	{
		find(c[x].left);
		find(c[x].right);
	}
}
void dog(int x,int y)
{
	int x1,y1,mid,tp;
	x1=x;
	y1=y;
	mid=b[(x+y)/2];
	do
	{
		while(b[x]<mid)
			x++;
		while(b[y]>mid)
			y--;
		if (x<=y)
		{
			tp=b[x];
			b[x]=b[y];
			b[y]=tp;
			x++;
			y--;
		}
	}while(x<y);
	if(x<y1)
		dog(x,y1);
	if(x1<y) 
		dog(x1,y);
}
void main()
{
	int i,g,tx,t,n,j;
	scanf("%d",&t);
	for(i=1;i<=t;i++)
	{
		scanf("%d",&n);
		g=0;
		for(j=1;j<=n;j++)
		{
			scanf("%d%d",&a[j][0],&a[j][1]);
			a[j][2]=j;
			b[++g]=a[j][0];
			b[++g]=a[j][1];
		}
		dog(1,2*n);
		tot=0;
		build(1,2*n);
		for(j=1;j<=n;j++)
			insert(a[j][0],a[j][1],1,a[j][2]);
		for(j=1;j<=n;j++)
			f[j]=0;
		find(1);
		tx=0;
		for(j=1;j<=n;j++)
			if(f[j]==1) 
				tx++;
		printf("%d\n",tx);
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值