P5490 [模板]扫描线 & 矩形面积并 & P1856 [IOI 1998 / USACO5.5] 矩形周长 Picture 题解

不知道为啥,这个知识点就是学的有点费劲 (

  • Pt1. P5490[模板]扫描线 & 矩形面积并

先想一个问题。给你一堆动态变化的线段,让你求它们的公共覆盖面积。

这是不是线段树维护一下就行(?

然后这个线段树的注意点是,由于我们维护的是线段而不是点,线段树节点会出现偏差。比如节点 2, 3 分别记录了 1~3 和 4~6 的信息,然后向上合并的时候就会漏掉 3~4 的情况。

额这个时候要把线段树节点映射的区间,右边界右移一位。这样节点 2,3 分别记录 1~4, 4~6,然后向上合并的时候就好用了。

这个时候看二维扫描线。

流程大概是这样的。先把二维图形抽象成两条边,一条入边,一条出边。横竖都可以,我这里是选择抽象成了两条竖线。然后你考虑一个竖线一个竖线往前走,这样可以把图形分割成若干部分。每个部分的横着的长度(即两条边间的距离)是一样的,因此只需要求出竖着的长度和就行了。

唉?这个是不是就跟最开始的问题一样了?我们每切换一条边,就动态修改一下。然后我们求出公共覆盖面积,再乘上横着的长度就行。

还是放一下代码。

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#define int long long
#define rep(i, a, b) for(int i = a; i <= b; ++i)
using namespace std;
using namespace __gnu_pbds;
gp_hash_table<int, int> mp;
constexpr int N = 100005;
int ans, tot, cnt, n, c[N << 3];
struct Line {
	int x, y1, y2, type;
	inline bool operator < (const Line rhs) const {
		return x < rhs.x;	
	}
}a[N << 3];
struct Sgement_Tree {
	int sum[N << 4], len[N << 4];
	void modify(int i, int l, int r, int L, int R, int k) {
		if(r < L || l > R) return;
		if(L <= l && r <= R) sum[i] += k;
		else {
			int mid = l + r >> 1;
			if(L <= mid) modify(i << 1, l, mid, L, R, k);
			if(R > mid) modify(i << 1 | 1, mid + 1, r, L, R, k);
		}
		if(sum[i]) len[i] = c[r + 1] - c[l];//这个其实是一个双设,相当于映射回去
		else len[i] = len[i << 1] + len[i << 1 | 1];
	}
}T;
signed main() {
	cin >> n;
	rep(i, 1, n) {
		int x1, y1, x2, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		a[++cnt] = {x1, y1, y2, 1ll};
		a[++cnt] = {x2, y1, y2, -1ll};
		c[++tot] = y1, c[++tot] = y2;
	}
	sort(c + 1, c + tot + 1);
	tot = unique(c + 1, c + tot + 1) - c - 1;
	rep(i, 1, tot) mp[c[i]] = i;
	sort(a + 1, a + cnt + 1);
	rep(i, 1, cnt) {
		T.modify(1, 1, tot + 1, mp[a[i].y1], mp[a[i].y2] - 1, a[i].type);
		//其他按照正常线段树来写,这样找出来的就是本来代表mp[a[i].y1], mp[a[i].y2] - 1的区间,通过右移映射得到现在的区间
		ans += T.len[1] * (a[i + 1].x - a[i].x);
	}
	cout << ans;
	return 0;
}
  • Pt2. P1856 [IOI 1998 / USACO5.5] 矩形周长 Picture

这两道题其实完全一样(存疑

第二道题甚至比第一道要好想的多。因为你考虑周长的边要不是竖着的,要不是横着的。

那我们横竖分别把它当成拆开的问题,算一下动态的覆盖长度不就好了吗?结果横竖相加即可。

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = a; i <= b; ++i)
using namespace std;
const int N = 5005;
int lst, ans, n, cnt, cnt1, tot, tot1, b[N << 3], d[N << 3];
struct Line {
	int x, y1, y2, type;
	inline bool operator < (const Line &rhs) const {
		if(x ^ rhs.x) {
			return x < rhs.x;
		}
		return type > rhs.type;
	}
}a[N << 3];
struct line {
	int y, x1, x2, type;
	inline bool operator < (const line &rhs) const {
		if(y ^ rhs.y) {
			return y < rhs.y;
		}
		return type > rhs.type;
	}
}c[N << 3];
struct Segemnt_Tree {
	int len[N << 4], sum[N << 4];
	inline void modify(int i, int l, int r, int L, int R, int k, int e[]) {
		if(r < L || l > R || l > r) return;
		int mid = l + r >> 1;
		if(L <= l && r <= R) sum[i] += k;
		else {	
			if(L <= mid) modify(i << 1, l, mid, L, R, k, e);
			if(R > mid) modify(i << 1 | 1, mid + 1, r, L, R, k, e);
		}
		if(sum[i]) len[i] = e[r + 1] - e[l];
		else len[i] = len[i << 1] + len[i << 1 | 1];
	}
}T, T1;
unordered_map<int, int> mp, mp1;
int main() {
	cin >> n;
	rep(i, 1, n) {
		int x1, x2, y1, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		a[++cnt] = {x1, y1, y2, 1};
		a[++cnt] = {x2, y1, y2, -1};
		c[++cnt1] = {y1, x1, x2, 1};
		c[++cnt1] = {y2, x1, x2, -1};
		b[++tot] = y1, b[++tot] = y2;
		d[++tot1] = x1, d[++tot1] = x2;
	}
	sort(a + 1, a + cnt + 1);
	sort(b + 1, b + tot + 1);
	tot = unique(b + 1, b + tot + 1) - b - 1;
	rep(i, 1, tot) mp[b[i]] = i;
	rep(i, 1, cnt) {
		T.modify(1, 1, tot + 1, mp[a[i].y1], mp[a[i].y2] - 1, a[i].type, b);
		ans += abs(T.len[1] - lst);
		lst = T.len[1];
	}
	sort(c + 1, c + cnt1 + 1);
	sort(d + 1, d + tot1 + 1);
	tot1 = unique(d + 1, d + tot1 + 1) - d - 1;
	rep(i, 1, tot1) mp1[d[i]] = i;
	lst = 0;
	rep(i, 1, cnt1) {
		T1.modify(1, 1, tot1 + 1, mp1[c[i].x1], mp1[c[i].x2] - 1, c[i].type, d);
		ans += abs(T1.len[1] - lst);//加个变化量就行
		lst = T1.len[1];
	}
	cout << ans;
	return 0;
}

没啥好解释的。不过,如果你 56 pts,请注意我们在一开始对边排序的时候要这么写,也就是先加后减。

if(x ^ rhs.x) {
      return x < rhs.x;
 }
      return type > rhs.type;

为啥捏?

如果你先减,那么如果给一部分减没了,那么在加的时候就会判断成你新增的变化量。这不就加多了吗。

posted @ 2025-09-07 22:55  風月華  阅读(26)  评论(0)    收藏  举报