CF 1401E Divide Square(扫描线,树状数组)

传送门

题目大意

  在平面直角坐标系中,区域 { ( x , y ) ∣ 0 ≤ x ≤ 1 e 6 , 0 ≤ y ≤ 1 e 6 } \{(x, y)|0 \leq x \leq 1e6, 0 \leq y\leq 1e6\} {(x,y)0x1e6,0y1e6}中,有n条水平的线段和m条竖直的线段,保证这些线段的某个端点一定在该区域的边界上并且同向线段之间没有重合的部分,问这些线段能将这个区域分为多少个部分。

解题思路

  首先我们应当发现如果存在一条水平线段与竖直线段相交,那么这两条线段一定能够划分出一个新区域(可以通过剪纸形象的理解一下,横着剪一刀竖着剪一刀,如果两刀痕相交一定能够剪下一块来)。那么答案就变成了初始区域的个数水平线段与竖直线段相交对数的和。
  对于求有多少对水平线段与竖直线段相交,一种解法是枚举每一条竖直的线段,将答案加上与这条竖直线段相交的水平线段的个数。那么如何求与之相交的水平线段的个数呢?
  利用类似于扫描线的思路,用平行于y轴的直线扫描所有横向线段,遇到横向线段的起始点就再其对应的纵坐标上加一,遇到横向线段结束时就减一。如果某个竖直线段( x = i x = i x=i上)与水平线段( y = j y = j y=j上)相交,那么当扫描线与 x = i x = i x=i重合时,扫描线的第j位一定是一。这样就可以用扫描线与竖直线段重合部分的区间和表示与其相交的水平线段的个数。

代码实现

注意: 利用树状数组维护单点修改区间查询问题时,维护的区间 包括0,应把所有区间端点加一再用其维护。
注意: 每一个两端点都在边界上的线段,是可以独立将区域分出一块的,这样的情况应当算在初始区域个数中。
具体细节请参考代码:

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e6 + 5;
const int B = 1e6;
// 树状数组 
struct BinaryIndexTree{
	int c[MAXN];
	inline int lowbit(int x) {
		return x & (-x);
	}
	inline void Modify(int x, int d) {
		for (; x < MAXN; x += lowbit(x)) c[x] += d;
	} 
	inline int Query(int x) {
		int ans = 0;
		for (; x; x -= lowbit(x)) ans += c[x];
		return ans;
	}
}bit;

// 横向线段端点(x, y),与在该点出需要进行的操作d(+1 \ -1) 
struct Node{
	int x, y, d;
	bool operator < (const Node rhs) const {
		return x < rhs.x;
	}
	Node(int x = 0, int y = 0, int d = 0) : x(x), y(y), d(d) {} 
}seq[MAXN];
// 竖向线段 (x, l) 到 (x, r) 
struct vLine{
	int x, l, r;
	bool operator < (const vLine rhs) const {
		return x < rhs.x; 
	}
	vLine(int x = 0, int l = 0, int r = 0) : x(x), l(l), r(r) {} 
}vln[MAXN];


int n, m, len;
// 最开始是整个一大快所以ans = 1 
long long ans = 1; 
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		int y, l, r;
		scanf("%lld%lld%lld", &y, &l, &r);
		if (l == 0 && r == B) ans++; // 两端点都在边界上 
		seq[++len] = Node(l, y, 1); // 起点加1 
		seq[++len] = Node(r + 1, y, -1); // 终点后减1 
	}
	
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%d", &vln[i].x, &vln[i].l, &vln[i].r);
		if (vln[i].l == 0 && vln[i].r == B) ans++;
	}
	
	// 此时ans存储的是初始区域的个数 
	sort(seq + 1, seq + len + 1);
	sort(vln + 1, vln + m + 1);
	
	for (int j = 0, i = 1; i <= m; i++) {
		while (j < 2 * n && seq[j + 1].x <= vln[i].x) {
			++j; bit.Modify(seq[j].y + 1, seq[j].d);
		}
		ans += bit.Query(vln[i].r + 1) - bit.Query(vln[i].l);
	}
	printf("%lld\n", ans);
	return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值