Scanning lines for area and perimeter (template, line segment tree)

Introduction

Quickly calculate the area or perimeter of multiple rectangular coverage areas.

achieve

Find area

The scan line of oi_wiki is explained very clearly, that is, the length of the bottom edge covered by each scan is multiplied by the sum of the height.
Mainly pay attention to the details of the code implementation. Here is a record of the discretization process of the line segment tree.
HDU1542-Atlantis

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;
#define endl '\n'
typedef long long ll;

const int N = 3000;

struct snode {
    double x, y1, y2;
    int flag;
    
};

bool cmp(const snode &a, const snode &b) {
    return a.x < b.x;
}

struct tnode {
    double sum, l, r;
};

tnode st[N << 2];
snode seg[N];
double num[N];
int lazy[N << 2];

void pushup(int rt) {
    if(lazy[rt] > 0) {
        st[rt].sum = st[rt].r - st[rt].l;
    } else {
        st[rt].sum = st[rt << 1].sum + st[rt << 1 | 1].sum;
    }
}

void update(double L, double R, int rt, int flag) {
    if(L == st[rt].l && R == st[rt].r) {
        lazy[rt] += flag;
        pushup(rt);
        return ;
    }
    if(st[rt << 1].r > L) //>而不是>=?不能等于,否则区间长度为0,会进入死循环。
        update(L, min(R, st[rt << 1].r), rt << 1, flag);
    if(st[rt << 1 | 1].l < R) 
        update(max(L, st[rt << 1 | 1].l), R, rt << 1 | 1, flag);
    pushup(rt);
}

void build(int lef, int rig, int rt) {
    if(rig - lef > 1) { //由于mid公用,所以>1就可以
        int mid = (lef + rig) / 2;
        st[rt].l = num[lef]; //离散化
        st[rt].r = num[rig];
        build(lef, mid, rt << 1);
        build(mid, rig, rt << 1 | 1); //mid而不是mid+1?因为是线段,头尾需要相连,否则答案会少值
        pushup(rt);
    } else {
        st[rt].l = num[lef];
        st[rt].r = num[rig];
        st[rt].sum = 0;
    }
}


int main() {
     //freopen("1.txt", "r", stdin);
     //freopen("test.txt", "w", stdout);
    
    ios::sync_with_stdio(false);
    int n;
    int cases = 0;
    while(1) {
        scanf("%d", &n);
        if(!n) break;
        cases++;
        for(int i = 0; i < n; i++) {
            double x1, x2, y1, y2;
            scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
            seg[i].x = x1; seg[i].y1 = y1; seg[i].y2 = y2; 
            seg[i].flag = 1;
            seg[i + n].x = x2; seg[i + n].y1 = y1; seg[i + n].y2 = y2; 
            seg[i + n].flag = -1;

            num[i + 1] = y1;
            num[i + 1 + n] = y2;
        }
        sort(num + 1, num + 1 + (2 * n)); //离散化
        sort(seg, seg + 2 * n, cmp);
        memset(lazy, 0, sizeof lazy);
        build(1, 2 * n, 1);
        double ans = 0;
        update(seg[0].y1, seg[0].y2, 1, seg[0].flag);
        for(int i = 1; i < 2 * n; i++) {
            ans += (seg[i].x - seg[i - 1].x) * st[1].sum;
            update(seg[i].y1, seg[i].y2, 1, seg[i].flag);
        }
        printf("Test case #%d\n", cases);
        printf("Total explored area: %.2lf\n\n", ans);
    }
}

Beg perimeter

The perimeter can be scanned first in the x-axis direction and then in the y-axis direction.
The specific method is to find the sum of the absolute value of the total length of the line segment before subtracting the total length of the line segment after covering the line segment is the answer.
After reading the blogs of the big brothers, there is also a way to get the total circumference by one scan, and there is time to make up.
POJ1177-Picture

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;
#define endl '\n'
typedef long long ll;

const int N = 5001;

struct snode {
    int x, y1, y2;
    int flag;
    
};

bool cmp(const snode &a, const snode &b) {
    return a.x < b.x;
}

struct tnode {
    int sum, l, r;
};

int ran[N << 1][4];
tnode st[N << 3];
snode seg[N << 1];
int num[N << 1];
int lazy[N << 3];

void pushup(int rt) {
    if(lazy[rt] > 0) {
        st[rt].sum = st[rt].r - st[rt].l;
    } else {
        st[rt].sum = st[rt << 1].sum + st[rt << 1 | 1].sum;
    }
}

void update(int L, int R, int rt, int flag) {
    if(L == st[rt].l && R == st[rt].r) {
        lazy[rt] += flag;
        pushup(rt);
        return ;
    }
    if(st[rt << 1].r > L) //>而不是>=?不能等于,否则长度为0,会进入死循环。
        update(L, min(R, st[rt << 1].r), rt << 1, flag);
    if(st[rt << 1 | 1].l < R) 
        update(max(L, st[rt << 1 | 1].l), R, rt << 1 | 1, flag);
    pushup(rt);
}

void build(int lef, int rig, int rt) {
    if(rig - lef > 1) { //由于mid公用,所以>1就可以
        int mid = (lef + rig) / 2;
        st[rt].l = num[lef];
        st[rt].r = num[rig];
        build(lef, mid, rt << 1);
        build(mid, rig, rt << 1 | 1); //mid而不是mid+1?因为是线段,头尾需要相连,否则会少值
        pushup(rt);
    } else {
        st[rt].l = num[lef];
        st[rt].r = num[rig];
        st[rt].sum = 0;
    }
}


int main() {
     //freopen("1.txt", "r", stdin);
     //freopen("test.txt", "w", stdout);
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        scanf("%d %d %d %d", &ran[i][0], &ran[i][1], &ran[i][2], &ran[i][3]);
    }
    for(int i = 0; i < n; i++) {
        int x1 = ran[i][0], y1 = ran[i][1], x2 = ran[i][2], y2 = ran[i][3];
        seg[i].x = x1; seg[i].y1 = y1; seg[i].y2 = y2; 
        seg[i].flag = 1;
        seg[i + n].x = x2; seg[i + n].y1 = y1; seg[i + n].y2 = y2; 
        seg[i + n].flag = -1;
        num[i + 1] = y1;
        num[i + 1 + n] = y2;
    }
    sort(num + 1, num + 1 + (2 * n));
    sort(seg, seg + 2 * n, cmp);
    memset(lazy, 0, sizeof lazy);
    build(1, 2 * n, 1);
    int ans1 = 0;
    //update(seg[0].y1, seg[0].y2, 1, seg[0].flag);
    for(int i = 0; i < 2 * n; i++) {
        int t = st[1].sum;
        update(seg[i].y1, seg[i].y2, 1, seg[i].flag);
        ans1 += abs(st[1].sum - t);
        
    }

    for(int i = 0; i < n; i++) {
        int x1 = ran[i][0], y1 = ran[i][1], x2 = ran[i][2], y2 = ran[i][3];
        seg[i].x = y1; seg[i].y1 = x1; seg[i].y2 = x2; 
        seg[i].flag = 1;
        seg[i + n].x = y2; seg[i + n].y1 = x1; seg[i + n].y2 = x2; 
        seg[i + n].flag = -1;
        num[i + 1] = x1;
        num[i + 1 + n] = x2;
    }
    sort(num + 1, num + 1 + (2 * n));
    sort(seg, seg + 2 * n, cmp);
    memset(lazy, 0, sizeof lazy);
    build(1, 2 * n, 1);
    //update(seg[0].y1, seg[0].y2, 1, seg[0].flag);
    int ans2 = 0;
    for(int i = 0; i < 2 * n; i++) {
        int t = st[1].sum;
        update(seg[i].y1, seg[i].y2, 1, seg[i].flag);
        ans2 += abs(st[1].sum - t);
    }
    cout << ans1 + ans2 << endl;
}

Guess you like

Origin www.cnblogs.com/limil/p/12701921.html