Picture POJ - 1177 线段树 扫描线

本文解析了一道关于计算多个矩形覆盖形成的多边形边长总和的问题,通过扫描线算法结合线段树的数据结构,实现了高效求解。文章详细介绍了算法流程,包括线段的拆分、维护及更新策略,以及如何计算最终的边长总和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解

题目大意 给若干个矩形问覆盖在一起的多边形的边长和是多少
使用扫描线将矩形拆分为线段从上往下扫 用线段树维护线段左右端点是否闭合计算线段分为几段 每次扫描时纵向边长为段数量2高度差 计算横向边时每次减上次的边长减去重复贡献

AC代码

#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;

struct node2
{
	int l, r, h, f;
	node2() {}
	node2(int a, int b, int c, int d) : l(a), r(b), h(c), f(d) {}
	bool operator < (const node2 &oth) const
	{
		return h < oth.h;
	}
}a[MAXN];
struct node
{
	int l, r, cnt;
	int s, k, lk, rk; //覆盖长度 分几段 左闭合 右闭合
}tre[MAXN * 4];
inline void PushUp(int x)
{
	if (tre[x].cnt)
	{
		tre[x].s = tre[x].r - tre[x].l + 1;
		tre[x].k = tre[x].lk = tre[x].rk = 1;
	}
	else
	{
		tre[x].s = tre[x << 1].s + tre[x << 1 | 1].s;
		tre[x].lk = tre[x << 1].lk, tre[x].rk = tre[x << 1 | 1].rk;
		tre[x].k = tre[x << 1].k + tre[x << 1 | 1].k - (tre[x << 1].rk && tre[x << 1 | 1].lk); //如果两个儿子中心都闭合则答案减1
	}
}
void Build(int x, int l, int r)
{
	tre[x].l = l, tre[x].r = r, tre[x].cnt = tre[x].k = tre[x].lk = tre[x].rk = 0;
	if (l == r)
		return;
	int m = l + r >> 1;
	Build(x << 1, l, m);
	Build(x << 1 | 1, m + 1, r);
}
void Update(int x, int pl, int pr, int v)
{
	int l = tre[x].l, r = tre[x].r;
	if (pl <= l && r <= pr)
	{
		tre[x].cnt += v;
		PushUp(x);
	}
	else
	{
		int m = l + r >> 1;
		if (pl <= m)
			Update(x << 1, pl, pr, v);
		if (pr > m)
			Update(x << 1 | 1, pl, pr, v);
		PushUp(x);
	}
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int N;
	cin >> N;
	for (int i = 1; i <= N; i++)
	{
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		x1 += 2e4, y1 += 2e4, x2 += 2e4, y2 += 2e4; //不离散化直接加2e4
		a[i * 2 - 1] = node2(y1, y2, x1, 1);
		a[i * 2] = node2(y1, y2, x2, -1);
	}
	N *= 2;
	ll ans = 0, last = 0;
	sort(a + 1, a + N + 1);
	Build(1, 0, 1e5);
	for (int i = 1; i <= N; i++)
	{
		Update(1, a[i].l, a[i].r - 1, a[i].f);
		if (i != N) //最后一次也要计算
			ans += (a[i + 1].h - a[i].h) * tre[1].k * 2; //计算纵向边长
		ans += abs(tre[1].s - last); //计算横向边长 要减去上次的长度和
		last = tre[1].s;
	}
	cout << ans << endl;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值