codeforces#337 D. Vika and Segments

D. Vika and Segments(離散+掃描+線段數)

                time limit per test2 seconds
            memory limit per test256 megabytes
                    inputstandard input
                    outputstandard output

Vika has an infinite sheet of squared paper. Initially all squares are white. She introduced a two-dimensional coordinate system on this sheet and drew n black horizontal and vertical segments parallel to the coordinate axes. All segments have width equal to 1 square, that means every segment occupy some set of neighbouring squares situated in one row or one column.

Your task is to calculate the number of painted cells. If a cell was painted more than once, it should be calculated exactly once.

Input
The first line of the input contains a single integer n (1 ≤ n ≤ 100 000) — the number of segments drawn by Vika.

Each of the next n lines contains four integers x1, y1, x2 and y2 ( - 109 ≤ x1, y1, x2, y2 ≤ 109) — the coordinates of the endpoints of the segments drawn by Vika. It is guaranteed that all the segments are parallel to coordinate axes. Segments may touch, overlap and even completely coincide.

Output
Print the number of cells painted by Vika. If a cell was painted more than once, it should be calculated exactly once in the answer.

Sample test(s)
input
3
0 1 2 1
1 4 1 2
0 3 2 3
output
8
input
4
-2 -1 2 -1
2 1 -2 1
-1 -2 -1 2
1 2 1 -2
output
16
Note
In the first sample Vika will paint squares (0, 1), (1, 1), (2, 1), (1, 2), (1, 3), (1, 4), (0, 3) and (2, 3).
題目大意:給出一定數量的線段,且這些線段的寬度爲1(即爲矩形),求這些線段包括的總點數(即這些矩形的總面積)。
解題思路:
1、修改每條線段最小的x座標和y座標,將其減一,變成求這些矩形的總面積。
2、每個矩形可以由兩條水平線段表示,上水平線和下水平線進行存儲(並標記1爲下水平線,-1爲上水平線)。
3、將這些水平線用其所處的高度進行從底端到頂端排序。
4、並用集合set記錄這些水平線左右兩個端點的座標,進行排序。
5、用線段樹表示每兩個x座標之間存在點的個數,並用cover數組記錄是否兩個x座標之間被覆蓋的次數進行更新。
6、開始對水平線從底端到頂端進行掃描,維持線段樹。
7、當爲下水平線時,就將其覆蓋的兩個x座標之間的數組覆蓋次數加一,並對其點數進行更新。當爲上水平線時,就將其覆蓋的兩個x座標之間的數組覆蓋次數減一,並對其點數進行更新。算出每層高度之間的總面積並進行相加求出總點數。
總結:
用線段樹存儲的好處在於每層高度之間記錄總的水平線長度總是最大的,並不受當前水平線影響,之前覆蓋過的水平線段仍能發揮其作用直到其對應的上水平線出現才失去作用,就算有水平線重疊也只計算一次。

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 100010;
struct lines {
    int x1;
    int x2;
    int h;
    int flag;
    friend bool operator< (lines a, lines b) {
        return a.h < b.h;
    }
};
long long cover[maxn<<4], sum[maxn<<4];
int w[maxn<<2], tot = 0, lenx = 0;
lines edges[maxn<<2];
void add_edge(int x, int x1, int h, int cover) { //存儲每個矩形的水平線
    edges[tot].x1 = x;
    edges[tot].x2 = x1;
    edges[tot].h = h;
    edges[tot++].flag = cover;
}
void unite() { //合併重合的x座標
    int k = lenx;
    lenx = 1;
    for (int i = 1; i < k; i++) {
        if (w[i] != w[i-1]) {
            w[lenx++] = w[i];
        }
    }
}
int binary_find(int x) { //查找對應的x座標,返回下標
    int l = 0, r = lenx;
    while (l <= r) {
        int mid = (l+r)/2;
        if (w[mid] == x)
            return mid;
        else if (w[mid] < x)
            l = mid+1;
        else
            r = mid-1;
    }
    return -1;
}
void maintain(int l, int r, int root) {
    if (cover[root] > 0) {
        sum[root] = w[r] - w[l];
    } else if (l == r-1) {
        sum[root] = 0;
    } else {
        sum[root] = sum[root<<1] + sum[root<<1 | 1];
    }
}
void query(int L, int R, int flag, int l, int r, int root) {
    if (L <= l && r <= R) {
        cover[root] += flag;
        maintain(l, r, root);
        return;
    } else if (r-1 == l) {
        return;
    }
    int mid = (l+r)/2;
    if (mid >= L) query(L, R, flag, l, mid, root << 1);
    if (mid < R) query(L, R, flag, mid, r, (root << 1 | 1));
    //正常的線段樹傳遞的是mid+1,而這裏結合實際意義需要傳遞mid,防止mid和mid+1之間有點數存在卻無法存儲之間的點數
    maintain(l, r, root);
}
int main() {
    int n, x1, x2, y1, y2;
    scanf("%d", &n);
    for (int i = 0;i < n; i++) {
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        if (x2 < x1 || y2 < y1) {
            swap(x1, x2);
            swap(y1, y2);
        }
        x1--;
        y1--;//將寬度變爲1,點數增加1,計算方便
        add_edge(x1, x2, y1, 1); // 上水平線賦值爲1
        add_edge(x1, x2, y2, -1);  //下水平線賦值爲-1
        w[lenx++] = x1;
        w[lenx++] = x2;
    }
    memset(cover, 0, sizeof(cover));
    memset(sum, 0, sizeof(sum));
    sort(w, w+lenx);//進行排序,用線段樹對其每兩個x座標之間存在的點數進行更新
    unite();
    sort(edges, edges+tot);
    long long ans = 0;
    for (int i = 0; i < tot-1; i++) {
        int L = binary_find(edges[i].x1);
        int R = binary_find(edges[i].x2);
        query(L, R, edges[i].flag, 0, lenx-1, 1);//更新線段樹
        ans += (edges[i+1].h - edges[i].h) * sum[1];//計算每層高度之間的總面積
    }
    printf("%lld\n", ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章