有N(100000)个长方形,并且它的边长平行于x轴或y轴(范围50000)。求这N个长方形覆盖的面积,重叠部分的面积只算一次。
在平面上的一些统计的题目,往往是用线段树,把x轴作为扫描线,然后把y轴映射到线段树上。这题也是如此:
若坐标范围太大,可以将其离散化,这题不需要。如图,有两个矩形。
竖细线作为扫描线,把y轴映射到线段树。在处理每一条扫描线的同时,首先统计线段树中已经被覆盖的总长度。这一次扫描的面积要加上:两条扫描线的之间的差 * 被覆盖的总长度。
下面是线段树上的值的变化:
x = 1:0 1 1 1 0 0 被覆盖总长度:3
x = 2:0 1 2 2 1 1 被覆盖总长度:4 面积加上:(2 - 1) * 3
x = 3:0 0 1 1 1 1 被覆盖总长度:4 面积加上:(3 - 2) * 4
x = 5:0 0 0 0 0 0 被覆盖总长度:0 面积加上:(5 - 3) * 4
现在需要维护被覆盖的总长度,我知道有两种方法:
一是统计线段树中,等于0的有多少个,记下最小值以维护,然后用y轴的总长度50000减去即可。二是对于线段树中每个结点所表示的区间被整段覆盖了多少次,记为cover,如果有一个操作是将这个区间整段覆盖(取消覆盖)一次,那么cover加一(减一),如果不是整段覆盖,那么就递归左子树或右子树。
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
inline int getInt()
{
int res = 0;
char ch;
do
ch = getchar();
while (ch < '0' || ch > '9');
do
{
res = res * 10 + (int) ch - (int) '0';
ch = getchar();
}
while (ch >= '0' && ch <= '9');
return res;
}
int n;
const int N = 100007;
const int L = 50007;
struct line
{
int x, y1, y2;
bool ins;
bool operator < (line const &o) const
{
return x < o.x;
}
}event[N << 1];
void Init()
{
n = getInt();
for (int i = 0; i < n; i ++)
{
event[i << 1].x = getInt();
event[i << 1].y1 = getInt();
event[(i << 1) ^ 1].x = getInt();
event[i << 1].y2 = getInt();
event[(i << 1) ^ 1].y1 = event[i << 1].y1;
event[(i << 1) ^ 1].y2 = event[i << 1].y2;
event[i << 1].ins = true;
event[(i << 1) ^ 1].ins = false;
}
sort(event, event + (n << 1));
}
struct Node
{
Node *lch, *rch;
int lo, hi;
int cnt, cov;
inline int size()
{
return hi - lo;
}
inline int mi()
{
return (lo + hi) >> 1;
}
}node[L << 1], *tree = &node[0];
int nnode = 1;
void Build(Node *&p, int lo, int hi)
{
p -> lo = lo;
p -> hi = hi;
if (lo + 1 == hi)
p -> lch = p -> rch = NULL;
else
{
int mi = p -> mi();
p -> lch = &node[nnode ++];
p -> rch = &node[nnode ++];
Build(p -> lch, lo, mi);
Build(p -> rch, mi, hi);
}
}
void Modify(Node *p, int le, int ri, bool ins)
{
if (le <= p -> lo && ri >= p -> hi)
if (ins)
{
p -> cov ++;
p -> cnt = p -> size();
}
else
{
p -> cov --;
if (p -> cov) p -> cnt = p -> size();
else
{
p -> cnt = 0;
if (p -> lch) p -> cnt += p -> lch -> cnt;
if (p -> rch) p -> cnt += p -> rch -> cnt;
}
}
else
{
int mi = p -> mi();
if (le < mi) Modify(p -> lch, le, ri, ins);
if (ri > mi) Modify(p -> rch, le, ri, ins);
if (p -> cov) p -> cnt = p -> size();
else p -> cnt = p -> lch -> cnt + p -> rch -> cnt;
}
}
void Solve()
{
ll ans = 0LL;
for (int i = 0; i < n << 1; i ++)
{
if (i) ans += (event[i].x - event[i - 1].x) * tree -> cnt;
Modify(tree, event[i].y1, event[i].y2, event[i].ins);
//printf("%d %d %I64d\n", i, tree -> cnt, ans);
}
printf("%I64d\n", ans);
}
int main()
{
freopen("area.in", "r", stdin);
freopen("area.out", "w", stdout);
Init();
Build(tree, 0, L + 1);
Solve();
return 0;
}