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;
}
posted @ 2018-10-21 22:08  Garen-Wang  阅读(141)  评论(0编辑  收藏  举报