[lnsyoj283/luoguP1856/IOI1998]矩形周长Picture
题意
原题链接
求几个矩形的周长并
sol
遇到几何图形的**并,都可以使用扫描线思想来解决
观察易得,与x轴平行的边和与y轴平行的边相互独立,因此可以扫描两次,分别计算并累加
以与x轴平行的边为例:
假设有一条平行于x轴的直线从下到上扫描,每当遇到一条边时,若这条边是某个矩形的下边,则在直线上覆盖这条边,否则就在直线上清除掉对应下边的覆盖标记。我们设这是第条边,在直线上有单位长度被覆盖,则第条边的贡献为,特别地,
证明
当遇到一条边时,可能会有两种情况:
- 这条边是下边,则这条边中处于矩形并的部分不会计算贡献,而不位于矩形并中的部分的贡献为
- 这条边是上边,则这条边处于除该矩形的所有矩形的并的部分不会计算贡献,而不位于该并的部分的贡献为
综上,该边的贡献为
因此,我们只需要处理以下三个操作:
- 在直线上的一个区间覆盖标记
- 在直线上删除一个区间的覆盖标记
- 查询被覆盖的单位长度
对于本题来说,由于数据较弱,因此这三个操作即便是的暴力也不会TLE,但对本题来说,仍可以使用线段树来优化到
我们可以在线段树上记录两个变量:表示该区间被覆盖的次数,表示该区间被覆盖的单位长度。在UPDATE时,我们可以将边对应的值直接加到中,若是矩形下边,该值就为,上边则为。然后,若这个区间被整体覆盖过,即,则就为区间长度,否则为两个儿子的之和
值得注意的是,由于单个点对于周长的贡献毫无意义,因此每个节点都应维护区间的贡献。为了达成这个目的,在UPDATE时要将右端点,在PUSHUP时再将其
同时,由于部分题目坐标为浮点数、坐标范围过大等原因,需要进行离散化(虽然本题不用)
最后,在查询时,只需要查询根节点的就可以了
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_map>
using namespace std;
const int N = 10005;
struct Line{
int pos, a, b, tag;
bool operator< (const Line W) const{
if (pos != W.pos) return pos < W.pos;
return tag > W.tag;
}
}row[N], col[N]; //扫描线
struct Node{
int val, cnt;
}treer[N * 4], treec[N * 4]; //线段树
int n;
int hai2vr[N], hai2vc[N]; //将下标转为值
int hav2i(int *hai2v, int n, int x){ //将值转换为下标
return lower_bound(hai2v, hai2v + n, x) - hai2v;
}
int discr(Line *lines, int *hai2v){ //离散化
sort(hai2v, hai2v + n);
int i, j;
for (i = 1, j = 1; i < n; i ++ )
if (hai2v[i] != hai2v[i - 1]) hai2v[j ++ ] = hai2v[i];
for (i = 0; i < n; i ++ ){
Line &line = lines[i];
line.a = hav2i(hai2v, j, line.a);
line.b = hav2i(hai2v, j, line.b);
}
return j;
}
void pushup(Node *tree, int *hai2v, int u, int l, int r){
if (tree[u].cnt) tree[u].val = hai2v[r + 1] - hai2v[l];
else tree[u].val = tree[u << 1].val + tree[u << 1 | 1].val;
}
void update(Node *tree, int *hai2v, int u, int l, int r, int L, int R, int val){
if (L <= l && r <= R){
tree[u].cnt += val;
pushup(tree, hai2v, u, l, r);
return ;
}
int mid = l + r >> 1;
if (L <= mid) update(tree, hai2v, u << 1, l, mid, L, R, val);
if (R > mid) update(tree, hai2v, u << 1 | 1, mid + 1, r, L, R, val);
pushup(tree, hai2v, u, l, r);
}
int solve(Line *lines, Node *tree, int *hai2v){ //解题主函数
int nn = discr(lines, hai2v);
sort(lines, lines + n);
int ans = 0;
for (int i = 0, last = 0; i < n; i ++ ){
Line line = lines[i];
update(tree, hai2v, 1, 0, nn - 1, line.a, line.b - 1, line.tag);
ans += abs(tree[1].val - last);
last = tree[1].val;
}
return ans;
}
int main(){
scanf("%d", &n);
for (int i = 0; i < n; i ++ ){
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
row[i * 2] = {y1, x1, x2, 1};
row[i * 2 + 1] = {y2, x1, x2, -1};
col[i * 2] = {x1, y1, y2, 1};
col[i * 2 + 1] = {x2, y1, y2, -1};
hai2vr[i * 2] = x1, hai2vr[i * 2 + 1] = x2;
hai2vc[i * 2] = y1, hai2vc[i * 2 + 1] = y2;
}
n *= 2;
printf("%d\n", solve(row, treer, hai2vr) + solve(col, treec, hai2vc));
}
蒟蒻犯的若至错误
- 线段树区间的叶子节点处理成了单点导致无效贡献
- UPDATE操作中,被完全覆盖没有
return
分类:
题解 / 2024训练
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现