线段树扫描求面积
作者:互联网
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010;
int n;
struct Segment
{
int x, y1, y2;
int k;
bool operator< (const Segment &t)const //按横坐标排序
{
return x < t.x;
}
}seg[N * 2]; //记录线段,包括横坐标,和两个纵坐标,k表示该线段是要进来的,还是要出去的
struct Node //线段树节点
{
int l, r; //记录左右边界
int cnt, len; //len记录本节点的覆盖长度(纵坐标方向)
}tr[N * 4]; //树一共有lg(N)+2层,也就是有4*N-1个节点
void pushup(int u) //利用本身和子节点的信息去更新自己
{
if (tr[u].cnt > 0) tr[u].len = tr[u].r - tr[u].l + 1; //如果前面加上k使得cnt大于零了,本段被覆盖了,正好修改
else if (tr[u].l == tr[u].r) tr[u].len = 0, tr[u].cnt = 0; //如果是叶子节点,len为0,cnt为0
else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len; //那现在就是cnt小于零而且不是叶子节点,也就是本段未被完全覆盖
//但是可能部分覆盖,那就是两个子节点覆盖范围之和
}
void build(int u, int l, int r) //建树,无需多言
{
// cout<<u<<endl;
tr[u] = {l, r};
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int l, int r, int k) //对树进行修整,u是修改的节点序号,l,r是范围,k表示是加入还是退出
{
if (tr[u].l >= l && tr[u].r <= r) //该节点已经完全被包括在修改的范围中
{
tr[u].cnt += k; //此段覆盖次数改变
pushup(u); //对上修改
}
else //未完全覆盖
{
int mid = tr[u].l + tr[u].r >> 1; //看看左右字节点还有哪个在范围中,也要修改
if (l <= mid) modify(u << 1, l, r, k);
if (r > mid) modify(u << 1 | 1, l, r, k);
pushup(u); //对上修改,这里pushup放在最后一行是关键,也就是pushup只根据子节点和自己的信息来更新自己
}
}
int main()
{
scanf("%d", &n);
int m = 0;
for (int i = 0; i < n; i ++ )
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
seg[m ++ ] = {x1, y1, y2, 1};
seg[m ++ ] = {x2, y1, y2, -1};
}
sort(seg, seg + m);
build(1, 0, 10000); //建树
int res = 0; //记录答案
for (int i = 0; i < m; i ++ )
{
if (i > 0) res += tr[1].len * (seg[i].x - seg[i - 1].x); //第一段当然不用算
modify(1, seg[i].y1, seg[i].y2 - 1, seg[i].k); //以后每一次等于加入本段时
} //y轴投影的覆盖长度乘以和上次的线段的距离
//然后加入本线段,对树进行修整
//这里写成y2-1是因为线段树看记录的最终是一个点,这里认为点的长度是1
printf("%d\n", res);
return 0;
}
标签:cnt,int,扫描,线段,面积,tr,len,seg 来源: https://www.cnblogs.com/sherkevin/p/16273765.html