【解题报告】POJ-2528 Mayor's posters 线段树+离散化

题目大意:有一面一千万长的墙,往墙上贴海报,海报的宽度给出。问最后一共能看到几面海报。

第一反应必然是线段树,但是一千万的线段树必然超内存,所以要离散化。因为我们其实只需要用到海报开始和结束的那两个位置。

这道题的难点在于如何离散化。

如果按照每个点来离散,会出现问题。就用其他题解给出的例子:

(1,10),(1,4),(6,10)。离散完就是1234四个叶子结点。

那么更新(1,4)时,1-2这段区间被覆盖,更新(6,10)时,3-4区间被覆盖。那么最后查询的时候会发现(1,10)被完全覆盖了。但是,事实上,应该有(5,5)这个区间没有被覆盖。

那么如何解决呢?

搜了网上的题解,说在x相差大于1时加入一个中间节点。在我看来,这就是一坨屎。

我的方法是,参考扫描线解题的过程。因为海报覆盖的是一个个点,我们把点变为块然后离散到数轴上,那么每一个点x都可以变为一段区间(x,x+1)。那么线段树存取什么呢,存取的是一段段区间。就拿上述例子来说,用我的离散方法,数轴被划分为1,5,6,11四个点。那么在线段树内就有三个叶子结点:(1,5)(5,6)(6,11)这样三个区间。这样就完美的解决了中间的5被覆盖的问题。

下面给出代码:

#include <iostream>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <set>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define lc n << 1
#define rc n << 1 | 1
const int maxp = 100005;//注意,用10005还是会数组超界,多开十倍即可
struct segtree {
	int l, r, id, lazy;//lazy表示这一段被染过色了
	int lx, rx;
	bool u;//u表示这个节点的子节点需不需要更新
}t[maxp << 3];
struct flag {
	int l, r;
}f[maxp];
int x[maxp << 2];
bool cs[maxp];
inline int getlen(int n) { return t[n].r - t[n].l + 1; }
inline void push_down(int n)
{
	t[lc].lazy = t[n].lazy;
	t[rc].lazy = t[n].lazy;
	t[lc].id = t[n].lazy;
	t[rc].id = t[n].lazy;
	t[lc].u = t[rc].u = true;
	t[n].u = false;
}
void build(int n,int l, int r)
{
	t[n].l = l, t[n].r = r;t[n].lazy = 1;t[n].u = false;
	t[n].lx = x[l], t[n].rx = x[r];
	t[n].id = 0;
	if (l + 1 == r) { return; }
	int mid = (l + r) >> 1;
	build(lc, l, mid);
	build(rc, mid, r);
}
void update(int n, int l, int r, int a)
{
	if (l > t[n].rx || r < t[n].lx)return;
	if (l <= t[n].lx && r >= t[n].rx)
	{
		t[n].lazy = a;t[n].u = true;
		t[n].id = a;
		return;
	}
	if(t[n].u)
		push_down(n);
	update(lc, l, r, a);
	update(rc, l, r, a);
}
int query(int n, int l, int r)
{
	if (l > t[n].rx || r < t[n].lx)return 0;
	if (l == t[n].lx && r == t[n].rx)
		return t[n].id;
	if(t[n].u)
		push_down(n);
	if (r <= t[lc].rx)return query(lc, l, r);
	if (l >= t[rc].lx)return query(rc, l, r);
}
int main()
{
	int T, n, q, l, r, temp;
	cin >> T;
	for (int C = 1;C <= T;C++)
	{
		set<int>LX;
		memset(x, 0, sizeof x);
		memset(cs, 0, sizeof cs);
		memset(t, 0, sizeof t);
		cin >> n;q = 0;
		for (int i = 1;i <= n;i++)
		{
			scanf("%d%d", &l, &r);
			f[i].l = l;f[i].r = r + 1;
			if (!LX.count(l))//这里使用set为了使区间不出现(x,x)这样的无意义区间
			{
				x[++q] = l;
				LX.insert(l);
			}
			if (!LX.count(r + 1))
			{
				x[++q] = r + 1;
				LX.insert(r + 1);
			}
		}
		sort(x + 1, x + q + 1);
		build(1, 1, q);
		for (int i = 1;i <= n;i++)
			update(1, f[i].l, f[i].r, i);
		int ans = 0;
		for (int i = 1;i < q;i++)
		{
			temp = query(1, x[i], x[i + 1]);
			if (cs[temp]||!temp)continue;
			cs[temp] = true;
			ans++;
		}
		cout << ans << endl;
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值