1. 程式人生 > >USACO Overplanting ( 線段樹掃描線 )

USACO Overplanting ( 線段樹掃描線 )

包括 int 引用 jpg com 相互 ref pan 二維

題意 : 在二維平面上給出 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; }
View Code

類似題目 : 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;
}
View Code

類似知識點 : 利用掃描線求矩形周長並

USACO Overplanting ( 線段樹掃描線 )