题解
题目大意 给若干个矩形问覆盖在一起的多边形的边长和是多少
使用扫描线将矩形拆分为线段从上往下扫 用线段树维护线段左右端点是否闭合计算线段分为几段 每次扫描时纵向边长为段数量2高度差 计算横向边时每次减上次的边长减去重复贡献
AC代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;
struct node2
{
int l, r, h, f;
node2() {}
node2(int a, int b, int c, int d) : l(a), r(b), h(c), f(d) {}
bool operator < (const node2 &oth) const
{
return h < oth.h;
}
}a[MAXN];
struct node
{
int l, r, cnt;
int s, k, lk, rk; //覆盖长度 分几段 左闭合 右闭合
}tre[MAXN * 4];
inline void PushUp(int x)
{
if (tre[x].cnt)
{
tre[x].s = tre[x].r - tre[x].l + 1;
tre[x].k = tre[x].lk = tre[x].rk = 1;
}
else
{
tre[x].s = tre[x << 1].s + tre[x << 1 | 1].s;
tre[x].lk = tre[x << 1].lk, tre[x].rk = tre[x << 1 | 1].rk;
tre[x].k = tre[x << 1].k + tre[x << 1 | 1].k - (tre[x << 1].rk && tre[x << 1 | 1].lk); //如果两个儿子中心都闭合则答案减1
}
}
void Build(int x, int l, int r)
{
tre[x].l = l, tre[x].r = r, tre[x].cnt = tre[x].k = tre[x].lk = tre[x].rk = 0;
if (l == r)
return;
int m = l + r >> 1;
Build(x << 1, l, m);
Build(x << 1 | 1, m + 1, r);
}
void Update(int x, int pl, int pr, int v)
{
int l = tre[x].l, r = tre[x].r;
if (pl <= l && r <= pr)
{
tre[x].cnt += v;
PushUp(x);
}
else
{
int m = l + r >> 1;
if (pl <= m)
Update(x << 1, pl, pr, v);
if (pr > m)
Update(x << 1 | 1, pl, pr, v);
PushUp(x);
}
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int N;
cin >> N;
for (int i = 1; i <= N; i++)
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
x1 += 2e4, y1 += 2e4, x2 += 2e4, y2 += 2e4; //不离散化直接加2e4
a[i * 2 - 1] = node2(y1, y2, x1, 1);
a[i * 2] = node2(y1, y2, x2, -1);
}
N *= 2;
ll ans = 0, last = 0;
sort(a + 1, a + N + 1);
Build(1, 0, 1e5);
for (int i = 1; i <= N; i++)
{
Update(1, a[i].l, a[i].r - 1, a[i].f);
if (i != N) //最后一次也要计算
ans += (a[i + 1].h - a[i].h) * tre[1].k * 2; //计算纵向边长
ans += abs(tre[1].s - last); //计算横向边长 要减去上次的长度和
last = tre[1].s;
}
cout << ans << endl;
return 0;
}