USACO Overplanting ( 线段树扫描线 )

题意 : 在二维平面上给出 N 个矩形,问你所有矩形构成的图案的面积是多少(相互覆盖的地方只计算一次)

分析 : 

求矩形面积并可以模拟来做,不过使用线段树来辅助做扫描线可以更高效地求解

扫描线顾名思义就是类似有一条线在二维平面上扫过去,将矩形面积并给扫出来

实现是使用线段树来模拟这个扫描的过程

第一步就是确定扫描的方向,是从左到右扫还是从上到下扫,这里以从上到下为例

第二步就是确定题目的坐标是否可能很大,如果很大意味着线段树开不了,则要进行离散化操作

由于是从上到下,我们记录每个矩形的上下两条边的一些信息

此后将不再考虑矩形,而是从上到下考虑这些横线

此图引用了 ==>http://blog.csdn.net/u013480600/article/details/22548393

信息包括有矩形上下两条边的左右端点的横坐标值,以及两条线的纵坐标的值即高度

然后我们给两条边的上边和下边分别做个标记,标记的作用就是判断当前矩形是要计入还是删除

在扫到当前的边为上边的时候意味着要在线段树内进行区间加法,将这条线段的值累计到线段树中

在扫到当前的边为下边的时候意味着要在线段树内进行区间减肥,将这条线段的值从线段树中删去

此时线段树在从上到下扫的过程中就一直记录着有效的横坐标值,记得刚刚我们存储的横线的高度么?

只要将有效的横坐标值(线段之长)乘以上下两条边的高度之差便得到了当前两条线段之间的面积

#include<bits/stdc++.h>
#define LL long long
#define lson l, m, rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn = 5e3 + 10;
const int Base = 1e8;
int add[maxn];
int x[maxn<<2];
long long sum[maxn<<2];
struct Node{
    int flag;
    int l, r, h;
    Node(){};
    Node(int L, int R, int H, int F):l(L),r(R),h(H),flag(F){};
    bool operator < (const Node & rhs) const{
        return this->h < rhs.h;
    };
}s[maxn];
inline void pushup(int rt, int l, int r)
{
    if(add[rt]) sum[rt] = x[r+1] - x[l];/// 这里每一个 l 和 r 是离散化后的值
                                        /// 所以应当代入 x 数组来获取真实值
    else if(l == r) sum[rt] = 0;
    else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

inline void update(int L, int R, int c, int l, int r, int rt)
{
    int m;
    if(L <= l && r <= R){
        add[rt] += c;
        pushup(rt, l, r);
        return ;
    }
    m = (l+r)>>1;
    if(L <= m) update(L, R, c, lson);
    if(R >  m) update(L, R, c, rson);
    pushup(rt, l, r);
}

int main(void)
{
    int n;
    scanf("%d", &n);
    int x1, x2, y1, y2;
    int num = 0;
    for(int i=0; i<n; i++){
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        x1 += Base, x2 += Base, y1 += Base, y2 += Base;/// 因为有负数坐标的存在,所以需要加上一个基数
        x[num] = x1;/// 记录所有出现的横坐标的值,方便离散化
        s[num++] = Node(x1, x2, y1, 1); /// 将所有的横边(与x轴平行)以及其高度存储起来
        x[num] = x2;
        s[num++] = Node(x1, x2, y2, -1);/// 顶边 flag == 1 而底边 flag == -1 是为了方便
                                        /// 从上到下扫描的时候做到,计入及删除这个矩形操作
    }
    sort(x, x+num);
    sort(s, s+num);
    int idx = std::unique(x, x+num) - x;/// 离散化横坐标
    int L, R;
    long long ans = 0;/// Attention !!!
    for(int i=0; i<num-1; i++){
        L = lower_bound(x, x+idx, s[i].l) - x;/// 找出线段树应当更新的左右界,注意是使用离散化后的值
        R = lower_bound(x, x+idx, s[i].r) - x - 1;
        update(L,R,s[i].flag,0,idx-1,1);/// 根据 flag 来确定是要删除还是添加操作
        ans+=(sum[1]*(1LL*s[i+1].h-1LL*s[i].h));/// 最后用当前存在的横坐标的总和去乘高度就是面积了,累加起来
    }
    printf("%lld\n", ans);
    return 0;
}

类似题目 : HDU 1542 Atlantis

#include<bits/stdc++.h>
#define LL long long
#define lson l, m, rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn = 505;
int add[maxn];
double x[maxn<<2], sum[maxn<<2];
struct Node{
    int flag;
    double l, r, h;
    Node(){};
    Node(double L, double R, double H, int F):l(L),r(R),h(H),flag(F){};
    bool operator < (const Node & rhs) const{
        return this->h < rhs.h;
    };
}s[maxn];
inline void pushup(int rt, int l, int r)
{
    if(add[rt]) sum[rt] = x[r+1] - x[l];
    else if(l == r) sum[rt] = 0;
    else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

inline void update(int L, int R, int c, int l, int r, int rt)
{
    int m;
    if(L <= l && r <= R){
        add[rt] += c;
        pushup(rt, l, r);
        return ;
    }
    m = (l+r)>>1;
    if(L <= m) update(L, R, c, lson);
    if(R >  m) update(L, R, c, rson);
    pushup(rt, l, r);
}

int main(void)
{
    int Case = 1, n;
    while(~scanf("%d", &n) && n){
        double x1, x2, y1, y2;
        int num = 0;
        for(int i=0; i<n; i++){
            scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
            x[num] = x1;
            s[num++] = Node(x1, x2, y1, 1);
            x[num] = x2;
            s[num++] = Node(x1, x2, y2, -1);
        }
        sort(x, x+num);
        sort(s, s+num);
        int idx = std::unique(x, x+num) - x;
        memset(add, 0, sizeof(add));
        memset(sum, 0, sizeof(sum));
        int L, R;
        double ans = 0;
        for(int i=0; i<num-1; i++){
            L = lower_bound(x, x+idx, s[i].l) - x;
            R = lower_bound(x, x+idx, s[i].r) - x - 1;
            update(L,R,s[i].flag,0,idx-1,1);
            ans+=(sum[1]*(s[i+1].h-s[i].h));
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n", Case++, ans);
    }
    return 0;
}

类似知识点 : 利用扫描线求矩形周长并

原文地址:https://www.cnblogs.com/Rubbishes/p/8323903.html

时间: 2024-08-30 17:27:22

USACO Overplanting ( 线段树扫描线 )的相关文章

【BZOJ】1382: [Baltic2001]Mars Maps (线段树+扫描线)

1382: [Baltic2001]Mars Maps Time Limit: 5 Sec  Memory Limit: 64 MB Description 给出N个矩形,N<=10000.其坐标不超过10^9.求其面积并 Input 先给出一个数字N,代表有N个矩形. 接下来N行,每行四个数,代表矩形的坐标. Output 输出面积并 Sample Input 2 10 10 20 20 15 15 25 30 Sample Output 225 本以为是傻逼题,没想到不容易啊- 线段树+扫描

BZOJ 4059 Cerc2012 Non-boring sequences 线段树+扫描线

题目大意:定义一个序列为[不无聊的]当且仅当这个序列的任意一个区间都存在一个数只出现过一次,给定一个序列,要求判断这个序列是否是[不无聊的] 定义lasti表示第i个元素上一次出现的位置(第一次出现则为0),nexti表示第i个元素下一次出现的位置(最后一次出现则为n+1),那么这个元素能成为某个区间仅出现一次的数,当且仅当这个区间的左端点在[lasti+1,i]之间,右端点在[i,nexti?1]之间 我们可以将区间的左右端点放在二维平面上,那么一个元素产生的贡献是一个矩形,我们要确定的是所有

HDU 4419 Colourful Rectangle --离散化+线段树扫描线

题意: 有三种颜色的矩形n个,不同颜色的矩形重叠会生成不同的颜色,总共有R,G,B,RG,RB,GB,RGB 7种颜色,问7种颜色每种颜色的面积. 解法: 很容易想到线段树扫描线求矩形面积并,但是如何维护每种颜色的长度着实让我伤透了脑筋.后来看了一位朋友的题解,才幡然醒悟. 开始想到了用二进制表示颜色,R用001表示,G用010表示,B用100表示.那么就可以用十进制1~7表示7种不同颜色了. 维护 cov[rt][1~3] 表示此区间内3种原色各有多少个, Len[rt][i]表示每种颜色的长

sgu316Kalevich Strikes Back(线段树+扫描线)

做法:总体想法是求出一个矩形的面积以及它所包含的矩形,然后用自己的面积减掉所包含的.主要问题是怎样求解它所包含的矩形. 因为是没有相交点的,可以利用扫描线的方法去做,类似染色,当前段如果是x色,也就是第x个矩形,那么再把他染成y颜色时,说明x包含y,而当扫到y的上边时,这一段又恢复到x色.标记一下被包含的矩形,记录所包含的矩形. 因为会有恢复染色操作,up需要时时更新,左儿子和右儿子一样颜色时就可以合并为一段. 1 ; 2 } 3 void down(int w) 4 { 5 if(s[w]!=

hdu1255 覆盖的面积 线段树-扫描线

矩形面积并 线段树-扫描线裸题 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include<math.h> 5 using namespace std; 6 const int maxm=2e3+5; 7 const double eps=1e-5; 8 9 struct seg{ 10 double x,y1,y2; 11 int c; 12 bool operator

线段树+扫描线 HDOJ 5091 Beam Cannon

题目传送门 1 /* 2 题意:给出若干个点的坐标,用一个矩形去覆盖,问最多能覆盖几个点 3 线段树+扫描线:思路是先建一棵以[y, y + h]的树,左右儿子[x, x + w] 4 以这棵树为范围,从左到右扫描,更新点数,x的+1, x+w的-1(超过矩形范围) 5 ans = 每次更新时所覆盖点数的最大值 6 */ 7 #include <cstdio> 8 #include <algorithm> 9 #include <iostream> 10 #includ

POJ 3277 City Horizon(线段树+扫描线+离散化)

题目地址:POJ 3277 水题..稍微处理一下然后用求面积并的方法求即可. 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <queue> #include <

[POI 2001+2014acm上海邀请赛]Gold Mine/Beam Cannon 线段树+扫描线

Description Byteman, one of the most deserving employee of The Goldmine of Byteland, is about to retire by the end of the year. The Goldmine management would like to reward him in acknowledgment of his conscientious work. As a reward Byteman may rece

hdu1542 Atlantis (线段树+扫描线+离散化)

Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 9032    Accepted Submission(s): 3873 Problem Description There are several ancient Greek texts that contain descriptions of the fabled i