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<=li或者tr[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<=li或者tr[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