亚特兰蒂斯中的Push_Up分析

csdn

0.前言

由于笔者翻阅了较多 b l o g blog blog,其中都未分析 P u s h U p PushUp PushUp的使用原理,在经过笔者 很 _很 长时间的思考后,想明白了 T a Ta Ta的使用原理,下面是我为清晰的思路,虽笔者斟酌了 很 _很 长时间,但由于能力问题,若有没讲清楚的地方,望不吝赐教或提出疑问, d a l a o dalao dalao轻喷

1.定义

当前节点为 p p p T a Ta Ta的父节点为 f a fa fa

l l l表示矩阵的左上角的 y y y坐标集合

r r r表示矩阵的右下角的 y y y坐标集合

t r [ p ] . c n t tr[p].cnt tr[p].cnt 表示 l i < = t r [ p ] . l , t r [ p ] . r < = r i , t r [ f a ] . l < = l i 或 者 t r [ f a ] . r > r i li <= tr[p].l, tr[p].r <= ri, tr[fa].l <=li 或者 tr[fa].r > ri li<=tr[p].l,tr[p].r<=ri,tr[fa].l<=litr[fa].r>ri 时, [ t r [ p ] . l , t r [ p ] . r ] [tr[p].l, tr[p].r] [tr[p].l,tr[p].r]被覆盖的次数

t r [ p ] . l e n tr[p].len tr[p].len 表示 l i < = t r [ p ] . l , t r [ p ] . r < = r i , t r [ f a ] . l < = l i 或 者 t r [ f a ] . r > r i li <= tr[p].l, tr[p].r <= ri, tr[fa].l <=li 或者 tr[fa].r > ri li<=tr[p].l,tr[p].r<=ri,tr[fa].l<=litr[fa].r>ri 时, [ t r [ p ] . l , t r [ p ] . r ] [tr[p].l, tr[p].r] [tr[p].l,tr[p].r]被覆盖的大小

2.分析

先放出代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

template <typename T>
void read (T &x) {
	x = 0; T f = 1;
	char tem = getchar ();
	
	while (tem < '0' || tem > '9') {
		if (tem == '-') f = -1;
		tem = getchar ();
	}
	
	while (tem >= '0' && tem <= '9') {
		x = (x << 1) + (x << 3) + tem - '0';
		tem = getchar ();
	}
	
	x *= f;
}

template <typename T>
void write (T x) {
	if (x < 0) {
		x = -x;
		putchar ('-');
	}
	if (x > 9) write (x / 10);
	putchar (x % 10 + '0');
}

const int MAXN = 1e5 + 5;

int n;

double rev[MAXN * 2];

int cnt;
struct date {
    double x, y_1, y_2;
	int flag;
    date () {}
    date (double X, double Y_1, double Y_2, int FLAG) {
        x = X, y_1 = Y_1, y_2 = Y_2, flag = FLAG;
    }
}a[MAXN * 2];

struct Segment_Tree {
    int l, r, cnt;
    double len;
}tr[MAXN * 4];

void Push_Up (int p) {
    if (tr[p].cnt) {
        tr[p].len = rev[tr[p].r + 1] - rev[tr[p].l];
    }
    else {
        tr[p].len = tr[p << 1].len + tr[p << 1 | 1].len;
    }
}

void Build (int p, int l, int r) {
    tr[p].l = l;
    tr[p].r = r;
    if (l == r) {
        tr[p].cnt = tr[p].len = 0;
        return;
    }
    
    int mid = (l + r) >> 1;
    Build (p << 1, l, mid);
    Build (p << 1 | 1, mid + 1, r);
    tr[p].cnt = tr[p].len = 0;
}

void Update (int p, int l, int r, int x) {
    if (l <= tr[p].l && tr[p].r <= r) {
        tr[p].cnt += x;
        Push_Up (p);
        return;
    }
    int mid = (tr[p].l + tr[p].r) >> 1;
    if (l <= mid) 
        Update (p << 1, l, r, x);
    if (r > mid)
        Update (p << 1 | 1, l, r, x);
    Push_Up (p);
}

bool cmp (date x, date y) {
    return x.x < y.x;
}

int main () {
    int T = 0;
    while (1) {
        read (n);
        if (n == 0) break;
        Build (1, 1, n * 2);
        cnt = 0;
        for (int i = 1; i <= n; i++) {
            double x_1, y_1, x_2, y_2;
            scanf ("%lf %lf %lf %lf", &x_1, &y_1, &x_2, &y_2);
            a[++cnt] = date (x_1, y_1, y_2, 1);
            rev[cnt] = y_1;
            a[++cnt] = date (x_2, y_1, y_2, -1);
            rev[cnt] = y_2;
        }
        
        sort (rev + 1, rev + 1 + cnt);
        int len = unique (rev + 1, rev + 1 + cnt) - rev - 1;
        
        sort (a + 1, a + 1 + cnt, cmp);
        double ans = 0;
        for (int i = 1; i <= cnt; i++) {
            if (i > 1) {
                ans += tr[1].len * (a[i].x - a[i - 1].x);
            }
            int l = lower_bound (rev + 1, rev + 1 + len, a[i].y_1) - rev;
            int r = lower_bound (rev + 1, rev + 1 + len, a[i].y_2) - rev;
            Update (1, l, r - 1, a[i].flag);
        }
        printf ("Test case #%d\n", ++T);
        printf ("Total explored area: %.2lf\n\n", ans);
    }
    return 0;
}

由于Build,离散化等操作十分基础,我只谈谈Push_Up(也是我卡了很久的点)

1. t r [ p ] . c n t ≠ 0 tr[p].cnt \neq 0 tr[p].cnt=0

十分简单,整个区间都被包含, t r [ p ] . l e n = r e v [ t r [ p ] . r + 1 ] − r e v [ t r [ p ] . l ] ; tr[p].len = rev[tr[p].r + 1] - rev[tr[p].l]; tr[p].len=rev[tr[p].r+1]rev[tr[p].l];

2. t r [ p ] . c n t = 0 tr[p].cnt = 0 tr[p].cnt=0 (重点)

首先我们可以明确,所有的祖先节点都没有被完全覆盖,那么我们得到所有矩阵都没有完全覆盖 [ t r [ p ] . l , t r [ p ] . r ] [tr[p].l, tr[p].r] [tr[p].l,tr[p].r](否则在祖先节点是就已经 r e t u r n return return了),所以我们只需要考虑子孙节点对 [ t r [ p ] . l , t r [ p ] . r ] [tr[p].l, tr[p].r] [tr[p].l,tr[p].r]的影响,又由于所有矩阵都没有完全覆盖 [ t r [ p ] . l , t r [ p ] . r ] [tr[p].l, tr[p].r] [tr[p].l,tr[p].r],所以 p p p节点的子节点只考虑Ta所代表的区间被完全覆盖,且父节点所代表的区间没有被完全覆盖的被覆盖的大小,我们可以发现,这就是 t r [ s o n ] . l e n tr[son].len tr[son].len

posted @ 2020-12-12 20:38  C2022lihan  阅读(50)  评论(0编辑  收藏  举报