POJ 1151 Atlantis & P1856 [USACO5.5]矩形周长Picture
扫描线+线段树求矩形并的面积和周长算法
扫描线其实十几天前就听说过了。但是不知道具体的实现方法。
这次专门刷了扫描线+线段树的模板题,好好地体会了一下。
一般会给你一些矩形的两个对角点的坐标,而这些坐标正常情况就会很大或者有负数,需要我们的离散化。
离散化这么一个操作就不多说了,排序去重,找个数组存就完事了。
在扫描矩形的时候,我们会适时地加入一条线段。如何维护这些线段?使用线段树!
注意:我们现在使用的线段树不是一般的维护点的线段树或者权值线段树,而是维护线段的线段树。
不同的最大的地方在于:一个节点的左右儿子分别会是\([l, mid]\)和\([mid, r]\)。不能+1,否则一段区间会被你忽略。
这里先给出线段树部分代码:
struct segTree
{
int l[maxn << 2], r[maxn << 2], cover[maxn << 2], len[maxn << 2];// 分别维护离散后的左右端点、覆盖次数、区间长度(我们实际用到的)
int ll[maxn << 2], rr[maxn << 2];// 离散前的左右端点
#define lson (root << 1)
#define rson (root << 1 | 1)
void build(int root, int L, int R)
{
l[root] = L, r[root] = R;
cover[root] = len[root] = 0;
ll[root] = lsh[L], rr[root] = lsh[R];
if(L + 1 == R) return;// 对于线段来说,这已经是极限了
int MID = (L + R) >> 1;
build(lson, L, MID);
build(rson, MID, R);// 不是mid + 1
}
void pushup(int root)
{
if(cover[root]) len[root] = rr[root] - ll[root];// 有线段覆盖过
else if(l[root] + 1 == r[root]) len[root] = 0; // 这是一条最短的线段
else len[root] = len[lson] + len[rson];// 这是最普通的pushup
}
void update(int root, Lines lin)// 这里的l、r和ll、rr不要混淆!
{
if(lin.l == ll[root] && lin.r == rr[root])// 根据我们的调整,总会有刚刚好的情况
{
cover[root] += lin.val;
pushup(root);
return;
}
if(lin.r <= rr[lson]) update(lson, lin);// 完全在左子树中
else if(ll[rson] <= lin.l) update(rson, lin); // 完全在右子树中
else// 横跨两个子树,分开处理
{
Lines temp = lin;
temp.r = rr[lson];// 左边的线段
update(lson, temp);
temp = lin;
temp.l = ll[rson];// 右边的线段
update(rson, temp);
}
pushup(root);// 记得更新!
}
} seg;
对于面积,我们是这么计算的:
对于每一次新的扫描,对答案加上 当前扫描后线段总长 乘以 这根线与前一根线的距离。(只需要从一个方向扫描)
对于周长,也比较简单:
对于每一次新的扫描,对答案加上 扫描前的线段总长与扫描后的线段总长 的绝对值。(需要两个方向扫描)
弄懂之后就不难了。本来我还在纳闷这个线段树如何实现。。。
代码:
POJ 1151:
#include<cstdio>
#include<algorithm>
const int maxn = 10005;
struct Rets
{
double left, up, right, down;
} s[maxn];
struct Lines
{
double l, r, y;
int val;
} line[maxn << 1];
double lsh[maxn << 1];
int len;
int n;
double ans;
struct segTree
{
int l[maxn << 2], r[maxn << 2];
double ll[maxn << 2], rr[maxn << 2];
int cover[maxn << 2];
double len[maxn << 2];
#define lson (root << 1)
#define rson (root << 1 | 1)
void build(int root, int _l, int _r)
{
l[root] = _l; r[root] = _r;
ll[root] = lsh[_l]; rr[root] = lsh[_r];
cover[root] = 0; len[root] = 0;
if(_l + 1 == _r) return;
int mid = (_l + _r) >> 1;
build(lson, _l, mid);
build(rson, mid, _r);
}
void pushup(int root)
{
if(cover[root]) len[root] = rr[root] - ll[root];
else if(l[root] + 1 == r[root]) len[root] = 0;
else len[root] = len[lson] + len[rson];
}
void update(int root, Lines lin)
{
if(lin.l == ll[root] && lin.r == rr[root])
{
cover[root] += lin.val;
pushup(root);
return;
}
if(lin.r <= rr[lson]) update(lson, lin);
else if(ll[rson] <= lin.l) update(rson, lin);
else
{
Lines temp = lin;
temp.r = rr[lson];
update(lson, temp);
temp = lin;
temp.l = ll[rson];
update(rson, temp);
}
pushup(root);
}
} seg;
bool cmp(Lines a, Lines b)
{
if(a.y == b.y) return a.val > b.val;
return a.y < b.y;
}
int main()
{
int kase = 0;
while(scanf("%d", &n) == 1 && n)
{
for(int i = 1; i <= n; i++) scanf("%lf%lf%lf%lf", &s[i].left, &s[i].up, &s[i].right, &s[i].down);
for(int i = 1; i <= n; i++) lsh[i] = s[i].left, lsh[i + n] = s[i].right;
std::sort(lsh + 1, lsh + n + n + 1);
len = std::unique(lsh + 1, lsh + n + n + 1) - lsh - 1;
for(int i = 1; i <= n; i++)
{
line[i].l = line[i + n].l = s[i].left;
line[i].r = line[i + n].r = s[i].right;
line[i].val = 1; line[i + n].val = -1;
line[i].y = s[i].down; line[i + n].y = s[i].up;
}
std::sort(line + 1, line + n + n + 1, cmp);
// solve it
seg.build(1, 1, len);
seg.update(1, line[1]);
ans = 0;
for(int i = 2; i <= n + n; i++)
{
ans += seg.len[1] * (line[i].y - line[i - 1].y);
seg.update(1, line[i]);
}
printf("Test case #%d\n", ++kase);
printf("Total explored area: %.2f\n\n", ans);// 要用%.2f还要两个换行有点坑
}
return 0;
}
P1856
#include<cstdio>
#include<cmath>
#include<algorithm>
const int maxn = 50005;
struct Juxings
{
int left, down, right, up;
} s[maxn];
struct Lines
{
int l, r, val, pos;
} line[maxn << 1];
int lsh[maxn << 1];
int len;
int n, ans;
struct segTree
{
int l[maxn << 2], r[maxn << 2], cover[maxn << 2], len[maxn << 2];
int ll[maxn << 2], rr[maxn << 2];
#define lson (root << 1)
#define rson (root << 1 | 1)
void build(int root, int L, int R)
{
l[root] = L, r[root] = R;
cover[root] = len[root] = 0;
ll[root] = lsh[L], rr[root] = lsh[R];
if(L + 1 == R) return;
int MID = (L + R) >> 1;
build(lson, L, MID);
build(rson, MID, R);
}
void pushup(int root)
{
if(cover[root]) len[root] = rr[root] - ll[root];
else if(l[root] + 1 == r[root]) len[root] = 0;
else len[root] = len[lson] + len[rson];
}
void update(int root, Lines lin)
{
if(lin.l == ll[root] && lin.r == rr[root])
{
cover[root] += lin.val;
pushup(root);
return;
}
if(lin.r <= rr[lson]) update(lson, lin);
else if(ll[rson] <= lin.l) update(rson, lin);
else
{
Lines temp = lin;
temp.r = rr[lson];
update(lson, temp);
temp = lin;
temp.l = ll[rson];
update(rson, temp);
}
pushup(root);
}
} seg;
bool cmp(Lines a, Lines b)
{
if(a.pos == b.pos) return a.val > b.val;
return a.pos < b.pos;
}
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
return s * ans;
}
int main()
{
n = read();
for(int i = 1; i <= n; i++) s[i].left = read(), s[i].down = read(), s[i].right = read(), s[i].up = read();
// from bot to top
for(int i = 1; i <= n; i++)
{
lsh[i] = s[i].left, lsh[i + n] = s[i].right;
line[i].l = line[i + n].l = s[i].left;
line[i].r = line[i + n].r = s[i].right;
line[i].pos = s[i].down; line[i].val = 1;
line[i + n].pos = s[i].up; line[i + n].val = -1;
}
std::sort(lsh + 1, lsh + n + n + 1);
len = std::unique(lsh + 1, lsh + n + n + 1) - lsh - 1;
std::sort(line + 1, line + n + n + 1, cmp);
seg.build(1, 1, len);
for(int i = 1; i <= n + n; i++)
{
int before = seg.len[1];
seg.update(1, line[i]);
ans += abs(seg.len[1] - before);
}
// from left to right
for(int i = 1; i <= n; i++)
{
lsh[i] = s[i].up, lsh[i + n] = s[i].down;
line[i].l = line[i + n].l = s[i].down;
line[i].r = line[i + n].r = s[i].up;
line[i].pos = s[i].left; line[i].val = 1;
line[i + n].pos = s[i].right; line[i + n].val = -1;
}
std::sort(lsh + 1, lsh + n + n + 1);
len = std::unique(lsh + 1, lsh + n + n + 1) - lsh - 1;
std::sort(line + 1, line + n + n + 1, cmp);
seg.build(1, 1, len);
for(int i = 1; i <= n + n; i++)
{
int before = seg.len[1];
seg.update(1, line[i]);
ans += abs(seg.len[1] - before);
}
// print it
printf("%d\n", ans);
return 0;
}