HDU_3340
这个题目要查询一个区间内的面积和,很容易联想到用线段树去做,但关键的问题在于由于有添加一个多边形的操作,那么我们如何下传标记去修改一个区间的面积和?
由于涉及到下传标记就涉及到标记叠加,因此我们要设法把面积的增量转化成用可以叠加的标记进行计算的形式。
在计算几何求面积的时候有一种方法是转换成梯形的有向面积累加计算,而梯形的面积主要取决于两个底和高的长度。对于这个题来讲,梯形高的长度就是x轴上区间的长度,这个很容易求得,而两个底的长度恰好就是我们想要的可以叠加的标记,因为(a+b)*h/2+(c+d)*h/2=((a+c)+(b+d))*h/2,而且根据两个底的长度就可以算出来一个区间内增加的这个梯形的有向面积的值,也可以计算出需要向左右儿子传递怎样的标记。
想到这,这个题的思路就有了。由于坐标值跨度大,我们可以先将问题都读入并保存起来,然后离散化x。用volume[]表示区间的面积和,同时需要两个标记dl[](梯形左底的变化量)、dr[](梯形右底的变化量),还可以再用一个标记flag[]表示这个区间是否被修改过。在下传左右底的变化量时,需要按比例计算出左右子树的dl[]、dr[]的变化的情况,这一点画个图就不难理解了。
#include<stdio.h> #include<string.h> #include<stdlib.h> #define MAXD 125010 double dl[4 * MAXD], dr[4 * MAXD], volume[4 * MAXD]; int N, tx[MAXD], X, flag[4 * MAXD]; struct Question { char b[5]; int n, x[10], y[10]; }question[MAXD]; int cmpint(const void *_p, const void *_q) { int *p = (int *)_p, *q = (int *)_q; return *p < *q ? -1 : 1; } void build(int cur, int x, int y) { int mid = (x + y) >> 1, ls = cur << 1, rs = (cur << 1) | 1; dl[cur] = dr[cur] = volume[cur] = flag[cur] = 0; if(x == y) return ; build(ls, x, mid); build(rs, mid + 1, y); } void init() { int i, j, k, n; scanf("%d", &N); k = 0; for(i = 0; i < N; i ++) { scanf("%s", question[i].b); if(question[i].b[0] == 'Q') { scanf("%d%d", &question[i].x[0], &question[i].y[0]); tx[k ++] = question[i].x[0], tx[k ++] = question[i].y[0]; } else { scanf("%d", &n); question[i].n = n; for(j = 0; j < n; j ++) { scanf("%d%d", &question[i].x[j], &question[i].y[j]); tx[k ++] = question[i].x[j]; } question[i].x[n] = question[i].x[0], question[i].y[n] = question[i].y[0]; } } qsort(tx, k, sizeof(tx[0]), cmpint); X = -1; for(i = 0; i < k; i ++) if(i == 0 || tx[i] != tx[i - 1]) tx[++ X] = tx[i]; build(1, 0, X - 1); } int BS(int x) { int mid, min = 0, max = X + 1; for(;;) { mid = (min + max) >> 1; if(mid == min) break; if(tx[mid] <= x) min = mid; else max = mid; } return mid; } double calculate(double x1, double y1, double x2, double y2, double x) { return ((x2 - x) * y1 + (x - x1) * y2) / (x2 - x1); } void pushdown(int cur, int x, int y) { int mid = (x + y) >> 1, ls = cur << 1, rs = (cur << 1) | 1; if(flag[cur]) { flag[ls] = flag[rs] = 1; double delta = calculate(tx[x], dl[cur], tx[y + 1], dr[cur], tx[mid + 1]); dl[ls] += dl[cur], dr[ls] += delta, volume[ls] += (dl[cur] + delta) * (tx[mid + 1] - tx[x]) / 2; dl[rs] += delta, dr[rs] += dr[cur], volume[rs] += (delta + dr[cur]) * (tx[y + 1] - tx[mid + 1]) / 2; dl[cur] = dr[cur] = flag[cur] = 0; } } void update(int cur) { volume[cur] = volume[cur << 1] + volume[(cur << 1) | 1]; } double query(int cur, int x, int y, int s, int t) { int mid = (x + y) >> 1, ls = cur << 1, rs = (cur << 1) | 1; if(x >= s && y <= t) return volume[cur]; pushdown(cur, x, y); if(mid >= t) return query(ls, x, mid, s, t); else if(mid + 1 <= s) return query(rs, mid + 1, y, s, t); else return query(ls, x, mid, s, t) + query(rs, mid + 1, y, s, t); } void refresh(int cur, int x, int y, int s, int t, double ys, double yt) { int mid = (x + y) >> 1, ls = cur << 1, rs = (cur << 1) | 1; if(x >= s && y <= t) { flag[cur] = 1; double dleft, dright; dleft = calculate(tx[s], ys, tx[t + 1], yt, tx[x]); dright = calculate(tx[s], ys, tx[t + 1], yt, tx[y + 1]); dl[cur] += dleft, dr[cur] += dright, volume[cur] += (dleft + dright) * (tx[y + 1] - tx[x]) / 2; return ; } pushdown(cur, x, y); if(mid >= s) refresh(ls, x, mid, s, t, ys, yt); if(mid + 1 <= t) refresh(rs, mid + 1, y, s, t, ys, yt); update(cur); } void solve() { int i, j, k, e, x1, y1, x2, y2; for(i = 0; i < N; i ++) { if(question[i].b[0] == 'Q') { j = BS(question[i].x[0]), k = BS(question[i].y[0]); if(j < k) printf("%.3f\n", query(1, 0, X - 1, j, k - 1)); else printf("0.000\n"); } else { for(e = 0; e < question[i].n; e ++) { j = BS(question[i].x[e]), k = BS(question[i].x[e + 1]); if(j < k) refresh(1, 0, X - 1, j, k - 1, -question[i].y[e], -question[i].y[e + 1]); else if(j > k) refresh(1, 0, X - 1, k, j - 1, question[i].y[e + 1], question[i].y[e]); } } } } int main() { int t; scanf("%d", &t); while(t --) { init(); solve(); } return 0; }