扫描线
扫描线问题主要利用了线段树。
因为矩形的并集比较难算,所以我们可以用(sum)(扫描线被截长度×所扫描的高度)来求和。而这样做发现可以用线段树来优化,具体优化方式如下:所扫描的高度比较好求,主要是扫描线被截长度需要优化。
我们可以设横边有一个a权值,如果该边是矩阵的下边则设为1,相反就设为-1,这样如果一段区间(如果该区间正好与横边对应的话至少会对应两个边)的没有被截(或者说已经扫过去,加入答案中去了)则此时该区间所对应的线段树的a权值和一定为0.
相反,如果该区间已经被扫描线扫进去且会更新答案的话,则a权值一定不为0。
然后枚举每一条边,在线段树中更新a值和被覆盖长度的值。
最后答案加上总区间被覆盖长度的值的和乘高度差。
#include <bits/stdc++.h>
#define int long long
#define ls l, mid, root << 1
#define rs mid + 1, r, root << 1 | 1
using namespace std;
int n, cnt, _x1, _x2, _y9, _y3, x[1001000], sum;
struct g {
int l, r, y, s;
bool operator < (g c) {
return y < c.y;
}
}data[1001000];
struct node {
int l, r, a, len;
}ans[4001000];
void build(int l, int r, int root) {
ans[root].l = l, ans[root].r = r;
ans[root].len = ans[root].a = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(ls);
build(rs);
return;
}
inline void pushup(int root)
{
if (ans[root].a)//如果当前已经被覆盖
ans[root].len = x[ans[root].r + 1] - x[ans[root].l];//那就完全被覆盖了,就直接加上就好。
else//没有被覆盖的就需要递归求解
ans[root].len = ans[root << 1].len + ans[root << 1 | 1].len;
}
void update(int ql, int qr, int root, int add)
{
int l = ans[root].l, r = ans[root].r;
if (x[l] >= qr || x[r + 1] <= ql) return;
if (ql <= x[l] && x[r + 1] <= qr)
{
ans[root].a += add;
pushup(root);//完成操作后记得要更新,查看是否被覆盖了
return;
}
update(ql, qr, root << 1, add);
update(ql, qr, root << 1 | 1, add);
pushup(root);
}
signed main()
{
scanf("%lld", &n);//初始化
for (int i = 1; i <= n; i++)
{
scanf("%lld%lld%lld%lld", &_x1, &_y9, &_x2, &_y3);
data[++cnt].l = _x1, data[cnt].r = _x2, data[cnt].s = 1, data[cnt].y = _y9;
x[cnt] = _x1;
data[++cnt].l = _x1, data[cnt].r = _x2, data[cnt].s = -1, data[cnt].y = _y3;
x[cnt] = _x2;
} //离散化
sort(data + 1, data + 1 + cnt);//离散化首先需要排序+去重
sort(x + 1, x + 1 + cnt);
int block = unique(x + 1, x + 1 + cnt) - (x + 1);
build(1, block - 1, 1); //block-1就可以包含所有的区间了。建树时的都是离散化之后的点
for (int i = 1; i < cnt; i++)
{
update(data[i].l, data[i].r, 1, data[i].s);//更新扫描线信息
sum += ans[1].len * (data[i + 1].y - data[i].y);//面积=底X高 ,更新面积,
}
printf("%lld", sum);
return 0;
}