覆蓋的面積 HDU - 1255 線段樹 掃描線
阿新 • • 發佈:2018-11-19
題解
使用線段樹+掃描線求解
將矩形分割為上下兩條邊 記錄邊的左端點和右端點分別對應矩形的左右側邊 上下邊的高度分別對應矩形的上下邊 和符號 上為正下為負
將邊按照高度從高到低排序處理 每次處理過程中根據符號線上段樹中離散化標記覆蓋範圍並計算覆蓋長度 長度分為覆蓋一次和兩次的長度分別計算
每次掃描用覆蓋兩次的長度乘上距離下條邊的高度差為當前分塊的答案貢獻 求和即可
AC程式碼
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 2e3 + 10; //*2
vector<double> dz; //離散化型別為double
struct node2
{
double l, r, h; //橫向邊
int f; //上下邊
bool operator < (const node2 &oth) const
{
return h < oth.h;
}
}a[MAXN];
struct node
{
int l, r, cnt; //葉節點覆蓋次數
double s1, s2; //覆蓋一次兩次的長度
}tre[MAXN * 4];
inline void PushUp(int x)
{
if (tre[x].cnt > 0) //只要被覆蓋則s1等於區間長
tre[x].s1 = dz[tre[x].r + 1] - dz[tre[x].l];
else if (tre[x].l == tre[x].r) //一個點為0
tre[x].s1 = 0;
else //不是一個點則合併兒子
tre[x].s1 = tre[x << 1].s1 + tre[x << 1 | 1].s1;
if (tre[x].cnt > 1) //兩次覆蓋則s2等於區間長
tre[x].s2 = dz[tre[x].r + 1] - dz[tre[x].l];
else if (tre[x].l == tre[x].r)
tre[x].s2 = 0;
else if (tre[x].cnt == 1) //如果覆蓋了一次 s2則為兒子s1的長度和
tre[x].s2 = tre[x << 1].s1 + tre[x << 1 | 1].s1;
else //如果一次都沒 考慮兒子的s2
tre[x].s2 = tre[x << 1].s2 + tre[x << 1 | 1].s2;
}
void Build(int x, int l, int r)
{
tre[x].l = l, tre[x].r = r, tre[x].cnt = tre[x].s1 = tre[x].s2 = 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) //每次只查詢根節點 不需要lzay和Query
{
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 Dis(double v)
{
return lower_bound(dz.begin(), dz.end(), v) - dz.begin();
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int T;
cin >> T;
while (T--)
{
dz.clear();
dz.push_back(-INF);
int N;
cin >> N;
for (int i = 1; i <= N; i++) //將矩形拆分為一正一負兩條橫向邊
{
double x1, y1, x2, y2;
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
a[i * 2 - 1] = { y1, y2, x1, 1};
a[i * 2] = { y1, y2, x2, -1};
dz.push_back(y1), dz.push_back(y2); //離散化縱座標
}
N *= 2;
sort(a + 1, a + N + 1); //按照高度從上到下排序
sort(dz.begin(), dz.end());
dz.erase(unique(dz.begin(), dz.end()), dz.end());
double ans = 0.0;
Build(1, 1, dz.size());
for (int i = 1; i < N; i++) //從上向下掃描 不掃描最後一個
{
Update(1, Dis(a[i].l), Dis(a[i].r) - 1, a[i].f); //更新[l, r)範圍
ans += (a[i + 1].h - a[i].h) * tre[1].s2; //到下一個線段的高度差*整個縱座標範圍的覆蓋面積
}
printf("%.2f\n", ans);
}
return 0;
}