poj 2528 Mayor's posters(离散化+线段树)

http://poj.org/problem?id=2528

题意:

市长竞选,每个市长都往墙上贴海报,海报之间彼此可以覆盖,给出粘贴顺序和每个海报的起点和终点,问最后又多少海报时可见的。


刚接触离散化,先写一写本人的理解:

如果原数据太大,建树会超内存。因此可以先对这些数排序,然后将它们映射成排序后的序号。比如在该题中,[1,4],[2,6],[8,10],[3,4],[7,10]; 先对这些区间端点去重(离散化要注意这一点,可以节省空间)排序后得  1,2,3,4,6,7,8,10 ;那么可以将这些数映射成其对应编号:

1        2        3        4        6        7         8        10

1        2        3        4        5        6         7         8


那么原区间就变成[1,4],[2,5],[7,8],[3,4],[6,8]; 这样建树只需要 8的空间,按区间端点数值建树需要10的空间。。如果数据更大,经过离散后会大大节省空间。


思路:

由于wall最大是1000 0000,直接用线段端点坐标建树肯定超内存。所以就像上面说的先离散化,再建树。之后,再从第一张开始贴,每贴一张就去更新相应区间的kind值。最后递归统计有多少个不同的kind值即可。


#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
#define LL long long
#define _LL __int64
#define eps 1e-12
#define PI acos(-1.0)
#define C 240
#define S 20
using namespace std;

const int maxn = 100010;

int n;
int np;

struct node
{
	int l,r;
	int val;
}post[10010],tree[80010];

int tmp[100010];
int f[10000010]; //映射函数
int vis[20010]; //用于统计海报数目
int cnt;

void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	tree[v].val = -1;
	if(l == r)
		return;
	int mid = (l+r) >> 1;
	build(v*2,l,mid);
	build(v*2+1,mid+1,r);
}

void update(int v, int l, int r, int val)
{
	if(tree[v].l == l && tree[v].r == r)
	{
		tree[v].val = val;
		return;
	}

	if(tree[v].val != -1)
	{
		int mid = (tree[v].l + tree[v].r) >> 1;
		update(v*2,tree[v].l,mid,tree[v].val);
		update(v*2+1,mid+1,tree[v].r,tree[v].val);
		tree[v].val = -1;
	}

	int mid = (tree[v].l + tree[v].r)>>1;
	if(r <= mid)
		update(v*2,l,r,val);
	else if(l > mid)
		update(v*2+1,l,r,val);
	else
	{
		update(v*2,l,mid,val);
		update(v*2+1,mid+1,r,val);
	}
}

int query(int v, int l, int r)
{
	if(tree[v].l == l && tree[v].r == r)
	{
		if(tree[v].val != -1 && !vis[tree[v].val])
		{
			vis[tree[v].val] = 1;
			return 1;
		}
		else if(tree[v].val == -1)
		{
			int mid = (tree[v].l + tree[v].r) >> 1;
			return query(v*2,tree[v].l,mid) + query(v*2+1,mid+1,tree[v].r);
		}
		else
			return 0;
	}
	int mid = (tree[v].l + tree[v].r) >> 1;
	if(r <= mid)
		return query(v*2,l,r);
	else if(l > mid)
		return query(v*2+1,l,r);
	else
		return query(v*2,l,mid) + query(v*2+1,mid+1,r);
}

int main()
{
	int test;
	scanf("%d",&test);

	while(test--)
	{
		scanf("%d",&n);
		np = 0;
		for(int i = 1; i <= n; i++)
		{
			scanf("%d %d",&post[i].l,&post[i].r);
			tmp[np++] = post[i].l;
			tmp[np++] = post[i].r;
		}

		sort(tmp,tmp+np);
		//坐标离散化并去重
		int k = 1;
		for(int i = 1; i < np; i++)
		{
			while(tmp[i] == tmp[k-1])
			{
				i++;
				if(i == np)
					break;
			}
			if(i == np)
				break;
			tmp[k++] = tmp[i];
		}
		np = k;
		for(int i = 0; i < np; i++)
		{
			f[tmp[i]] = i + 1;
		}
		build(1,1,np);
		for(int i = 1; i <= n; i++)
		{
			update(1,f[post[i].l],f[post[i].r],i);
		}
		memset(vis,0,sizeof(vis));
		int ans = query(1,1,np);
		printf("%d\n",ans);
		
	}
	return 0;
}



1、  线段树是二叉树,且必定是平衡二叉树,但不一定是完全二叉树。

2、  对于区间[a,b],令mid=(a+b)/2,则其左子树为[a,mid],右子树为[mid+1,b],当a==b时,该区间为线段树的叶子,无需继续往下划分。

3、  线段树虽然不是完全二叉树,但是可以用完全二叉树的方式去构造并存储它,只是最后一层可能存在某些叶子与叶子之间出现“空叶子”,这个无需理会,同样给空叶子按顺序编号,在遍历线段树时当判断到a==b时就认为到了叶子,“空叶子”永远也不会遍历到。

4、  之所以要用完全二叉树的方式去存储线段树,是为了提高在插入线段和搜索时的效率。用p*2,p*2+1的索引方式检索p的左右子树要比指针快得多。

5、线段树的精髓是,能不往下搜索,就不要往下搜索,尽可能利用子树的根的信息去获取整棵子树的信息。如果在插入线段或检索特征值时,每次都非要搜索到叶子,还不如直接建一棵普通树更来得方便。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值