• 题解 POJ1151【Atlantis】


    题目大意

    给你n个矩形,让你求所有矩形覆盖的总面积,重叠的地方只算一次

    输入格式

    输入的第一行包含一个整数n,表示可得到的地图数目。

    以下n行,每行描述一张地图。每行包含4个整数x1,y1,x2和y2(0≤x1<x2≤30000,0≤y1<y2≤30000)。数值(x1,y1)和(x2,y2)是坐标,分别表示绘制区域的左下角和右上角坐标。每张地图是矩形的,并且它的边是平行于x坐标轴或y坐标轴的。

    数据可能有多组,当n为零时停止输入

    输出格式

    对于每个测试数据,你的程序应该输出一个答案。每个答案的第一行必须是“Test case #k”,其中k是测试数据的编号(从1开始)。第二个必须是“Total explored area: a”,其中a是总探索面积(即在本测试案例中所有矩形的并集面积),精确到小数点右边两位。
    在每个测试用例后输出一个空行。

    样例输入

    2
    10 10 20 20
    15 15 25 25.5
    0
    

    样例输出

     Test case #1
     Total explored area: 180.00 
    

    数据范围

     n <= 100
    

    主要思路:线段树 + 扫描线

    这是一道扫描线裸题。

    我们把要覆盖的矩形抽象成两个线,这两条线就被称为扫描线。如图(网上扒拉来的图QwQ,若有侵权请联系博主删除QwQ)

    对于扫描线,我们可以把扫描线的高度进行离散化。然后我们把x轴上的线(图中的黑线)分隔开的就可以抽象成几段线段。

    我们只需要把这些交点用线段树维护就好。然后我们把所有扫描线从最小到最大排序一下。我们首先记录一条扫描线的左端点与右端点,它的高度,它是下底边还是上界边,如果是下底边就记为1,上界边记为-1。(这里实际是做了个差分,就是维护两条扫描线穿过(或切于边界的)矩形有多少。

    线段树中维护的是这段区间的下底边个数和下底边总长度。我们扫描的作是每次更新下底边总长度和下底边个数增加新面积。我们只要从最下向上扫描一遍统计总面积就好了。

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <ctime>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <map>
    #include <vector>
    using namespace std;
    #define go(i, j, n, k) for(int i = j; i <= n; i += k)
    #define fo(i, j, n, k) for(int i = j; i >= n; i -= k)
    #define inf 1 << 30
    #define mn 100010
    #define ll long long
    inline int read() {
    	int x = 0, f = 1; char ch = getchar();
    	while(ch > '9' || ch < '0') { if(ch == '-') f = -f; ch = getchar(); }
    	while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    	return x * f;
    }
    struct tree{
    	int mark; double sum;
    } z[mn << 2];
    struct seg{
    	double l, r, h;
    	int d;
    	seg() {}
    	seg(double _l, double _r, double _h, int _d) : l(_l), r(_r), h(_h), d(_d) {}
    	bool operator < (const seg &b) const { return h < b.h; }
    } s[mn];
    int n, num, kkk;
    double ha[mn];
    double x, y, xx, yy;
    #define root 0, m - 1, 1
    #define lson l, m, rt << 1
    #define rson m + 1, r, rt << 1 | 1
    #define bson l, r, rt
    inline void update(int l, int r, int rt) {
    	if(z[rt].mark) z[rt].sum = ha[r + 1] - ha[l];
    	else if(l == r) z[rt].sum = 0;
    	else z[rt].sum = z[rt << 1].sum + z[rt << 1 | 1].sum;
    }
    inline void modify(int l, int r, int rt, int nowl, int nowr, int d) {
    	if(nowl <= l && r <= nowr) {
    		z[rt].mark += d;
    		update(bson);
    		return;
    	}
    	int m = (l + r) >> 1;
    	if(nowl <= m) modify(lson, nowl, nowr, d);
    	if(m < nowr)  modify(rson, nowl, nowr, d);
    	update(bson);
    } 
    inline int search(double key, double* x, int n) {
    	int l = 0, r = n - 1;
    	while(l <= r) {
    		int m = (l + r) >> 1;
    		if(x[m] == key) return m;
    		if(x[m] > key) r = m - 1;
    		else l = m + 1; 
    	}
    	return -1;
    }
    int main() {
    	while(cin >> n, n) {
    		num = 0;
    		go(i, 0, n - 1, 1) {
    			cin >> x >> y >> xx >> yy;
    			ha[num] = x;
    			s[num++] = seg(x, xx, y, 1);
    			ha[num] = xx;
    			s[num++] = seg(x, xx, yy, -1);
    		}
    		sort(ha, ha + num);
    		sort(s, s + num);
    		int m = 1;
    		go(i, 1, num - 1, 1) 
    			if(ha[i] != ha[i - 1]) ha[m++] = ha[i];
    		double ans = 0;
    		go(i, 0, num - 1, 1) {
    			int L = search(s[i].l, ha, m);
    			int R = search(s[i].r, ha, m) - 1;
    			modify(root, L, R, s[i].d);
    			ans += z[1].sum * (s[i + 1].h - s[i].h);
    		}
    		printf("Test case #%d
    Total explored area: %.2lf
    ", ++kkk, ans);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    UI自动化之鼠标、键盘事件
    iframe框中元素定位
    接口 Interface
    序列化和反序列化
    密封类和部分类
    简单工场设计模式
    ADO.NET数据库操作
    集合
    里氏转换
    装箱和拆箱
  • 原文地址:https://www.cnblogs.com/yizimi/p/10056346.html
Copyright © 2020-2023  润新知