洛谷P1856系列题解

博客解析了洛谷P1856题目的解题思路,将矩形转化为线段进行处理,利用线段树维护数轴上的线段覆盖情况。通过线段树的插入、删除和查询操作,实现了O(nlogm)的时间复杂度解题。文章详细阐述了线段树节点的维护方式和操作3的正确性分析,涉及永久化标记的概念。
摘要由CSDN通过智能技术生成

题目链接

题目大意:给你n个矩形,求它们的周长并,n ≤ \le 5e3,坐标范围[-1e4, 1e4]。

难度:Ag+

分析:

每个矩形可以拆成四条线段,两条水平,两条竖直。对于一个矩形拆成的两条水平线段,其左右端点的横坐标相同,纵坐标不同,把纵坐标小的叫第一类水平线段,纵坐标大的叫第二类水平线段。首先维护一个数轴,其上所有点均未被线段覆盖。然后对于所有水平的线段,按线段的纵坐标从小到大排序。遍历排好序的水平线段,对于第一类水平线段,将其插入到数轴上,对于第二类水平线段,将其对应的第一类水平线段从数轴上删除。每次插入和删除之后,统计数轴中有多少点被至少一条线段覆盖,其值和上一次统计的结果做差,差的绝对值就是对答案的贡献。(至于原因,可以画图来看一下)

对于竖直的线段,可以类比水平线段的做法。

现在问题转换成,一个数轴,三种操作:

  1. 在数轴上插入一条线段
  2. 在数轴上删除一条线段
  3. 查询数轴上有多少个点被至少一条线段覆盖

发现可以使用线段树来维护这个数轴,线段树每个节点维护两个值a和b,a表示区间里被至少一条线段覆盖的点数,b表示区间被直接覆盖的次数。

对于操作1,线段对应的区间,可以在线段树上被拆分成一些子区间,这些子区间的对应节点的b值加1,由于这些子区间里每一个点现在都被至少一条线段覆盖了,对应节点的a值变为区间长度。

对于操作2,同操作1一样把线段对应的区间拆分成子区间,这些子区间的对应节点的b值减1,如果某个叶节点的b值被减为0,显然应该把这个叶节点的a值也赋为0,如果某个非叶节点的b值被减为0,则该节点的a值等于其左右子节点的a值之和(因为虽然没有线段直接覆盖该区间,但有可能直接覆盖该区间的子区间)。

对于操作3,直接返回根节点的a值。

复杂度:O(nlogm),n表示矩形数,m表示坐标范围

代码:

# include <bits/stdc++.h>
# define MAXN 20005

using namespace std;

struct Line
{
   
	int pos, x, y, val;	//pos表示线段的横(纵)坐标,x和y分别表示两个端点的纵(横)坐标,val表示应该插入还是删除

	bool operator < (const Line & l) const
	{
   
		if (pos == l.pos)
			return val > l.val;	//先插入,再删除
		return pos < l.pos;
	}
};

struct SGT
{
   
	int n;
	int a[2][MAXN << 2];	//a[0]表示区间里被覆盖的点数,a[1]表示区间直接被覆盖的次数

	void build(int size)
	{
   
		n = size;
		for (int i = 0; i < 2; ++i)
			memset(a[i
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值