POJ 2528 - Mayor's posters 线段树+离散化

%%%
//感觉博主对于离散的解释很好,很感谢。
%%%
//博主解释了一下本题离散的特殊性,感谢

如三张海报为: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。
离散化时 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。


这里说一下对于代码的理解
if (left>mid)
update(i * 2 + 1, left, right, val);
else if (right <= mid)
update(i * 2, left, right, val);
else
{
update(i * 2, left, mid, val);
update(i * 2 + 1, mid + 1, right, val);
}
这里目标区间的左端点大于mid,则需要向右节点进行更新(因为右节点的区间左端点更大)
同理当(right<=mid)或者(left<=mid <=right)时也是如此

代码是之前从一位dalao那看完后,模仿的,但是之前一直没有写这道题目的总结,忘了收藏dalao的博客,很抱歉.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct point {
	int l, r;
	int mark, sum;
};
point tree[10001 * 4 * 4], num[10001 * 4];
int ans, res[10001 * 4], vis[10001 * 4];
void build(int i, int left, int right)
{
	tree[i].l = left, tree[i].r = right;
	tree[i].mark = tree[i].sum = 0;
	if (left == right) return;
	int mid = (left + right) / 2;
	build(i * 2, left, mid);
	build(i * 2 + 1, mid + 1, right);
}
void update(int i, int left, int right, int val)
{
	if (left <= tree[i].l&&tree[i].r <= right) {
		tree[i].mark = tree[i].sum = val; return;
	}
	//等同于lazy
	if (tree[i].mark)
	{
		tree[i * 2].mark = tree[i * 2 + 1].mark = tree[i].mark;
		tree[i * 2].sum = tree[i * 2 + 1].sum = tree[i].mark;
		tree[i].mark = 0;
	}
	int mid = (tree[i].r + tree[i].l) / 2;
	//此时的mid取决于i所以要判断mid>left,right<= 的情况。
	if (left>mid) update(i * 2 + 1, left, right, val);
	else if (right <= mid) update(i * 2, left, right, val);
	else
	{
		update(i * 2, left, mid, val);
		update(i * 2 + 1, mid + 1, right, val);
	}
}

//查找
int find(int i)
{
	if (tree[i].l == tree[i].r)
	{
		if (!vis[tree[i].sum])//没被计算过
		{
			vis[tree[i].sum] = 1;
			++ans;
		}

		return ans;
	}
	//等同于lazy,即延迟更新
	if (tree[i].mark)
	{
		tree[i * 2].mark = tree[i * 2 + 1].mark = tree[i].mark;
		tree[i * 2].sum = tree[i * 2 + 1].sum = tree[i].mark;
		tree[i].mark = 0;
	}
	find(i * 2);
	find(i * 2 + 1);
}
int main()
{
	int t, i, n, tn, tn1, powr, powl;
	scanf("%d", &t);

	if (t == 0)
		return 0;
	while (t--)
	{
		res[0] = 0;
		scanf("%d", &n);
		for (i = 1; i <= n; i++)
		{
			scanf("%d %d", &num[i].l, &num[i].r);
			if (num[i].l>num[i].r) swap(num[i].l, num[i].r);
			res[++res[0]] = num[i].l;
			res[++res[0]] = num[i].r;
		}
		sort(res + 1, res + res[0] + 1);
		//离散化
		tn1 = tn = unique(res + 1, res + res[0] + 1) - res;//去重后得到范围
		for (i = 2; i<tn; i++)//插入数
			if (res[i] - res[i - 1]>1)
				res[tn1++] = res[i] - 1;
		//每个相邻的差值大于1的时候要插入一个数(就插入x[i]-1好了)
		res[0] = tn1 - 1;//插入后的数据个数
		sort(res + 1, res + res[0] + 1);
		build(1, 1, res[0]);//以新的区间(映射后的范围)建树
		for (i = 1; i <= n; i++)
		{
			powl = lower_bound(res + 1, res + 1 + res[0], num[i].l) - res;
			powr = lower_bound(res + 1, res + 1 + res[0], num[i].r) - res;
			//映射后所在位置,进行数据的更新
			update(1, powl, powr, i);
		}
		ans = 0;
		memset(vis, 0, sizeof(vis));
		vis[0] = 1;
		printf("%d\n", find(1));
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值