poj-2991

来源:互联网 发布:照片审核处理工具 mac 编辑:程序博客网 时间:2024/06/06 23:16
    // 1260K   985MS   C++#include <stdio.h>#include <string.h>#include <math.h>const int MAX = 10010;const double PI = acos(-1.0);double getrad(int x) {    return x * PI / 180;}struct TreeNode {    int left;    int right;    double X;    double Y;    int angle;    int lazyRotation;};typedef struct TreeNode TreeNode;TreeNode tree[MAX<<2];int curDegree[MAX];void buildTree(int pos, int begin, int end) {    TreeNode & curNode = tree[pos];    curNode.left = begin;    curNode.right = end;    curNode.X = 0;    curNode.Y = 0;    curNode.angle = 0;    curNode.lazyRotation = 0;    if (begin == end) {        return;    } else {        int mid = (begin + end)>>1;        buildTree(pos<<1, begin, mid);        buildTree(pos<<1|1, mid+1, end);    }}int segmentsNum;int opNum;void Rotate(double &dx, double &dy, double rad){    double x = dx, y = dy;    dx = x * cos(rad) - y * sin(rad);    dy = x * sin(rad) + y * cos(rad);}void pushDown(int pos) {    TreeNode & curNode = tree[pos];    int curX = curNode.X;    int curY = curNode.Y;    int curLazyRotation = curNode.lazyRotation;    if (curLazyRotation && (curNode.left < curNode.right)) { // need some rotation        TreeNode & leftNode = tree[pos<<1];        TreeNode & rightNode = tree[pos<<1|1];                leftNode.angle += curLazyRotation;        rightNode.angle += curLazyRotation;        leftNode.angle %= 360;        rightNode.angle %= 360;        Rotate(leftNode.X, leftNode.Y, getrad(curLazyRotation));        // leftNode.X += curX;        // leftNode.Y += curY;        Rotate(rightNode.X, rightNode.Y, getrad(curLazyRotation));        // rightNode.X += leftNode.X;        // rightNode.Y += leftNode.Y;        leftNode.lazyRotation += curLazyRotation;        rightNode.lazyRotation += curLazyRotation;        leftNode.lazyRotation %= 360;        rightNode.lazyRotation %= 360;        curNode.lazyRotation = 0;    }}void pushUp(int pos) {    if (pos == 1) {        return;    }    int parentPos = pos>>1;    TreeNode & curNode = tree[parentPos];    // printf("pushUp %d %d\n", curNode.left, curNode.right);    TreeNode & leftNode = tree[parentPos<<1];    TreeNode & rightNode = tree[parentPos<<1|1];    curNode.X = leftNode.X + rightNode.X;    curNode.Y = leftNode.Y + rightNode.Y;}void updateSegmentWithY(int pos, int rangeLeft, int rangeRight, int Y) {    TreeNode & curNode = tree[pos];    int left = curNode.left;    int right = curNode.right;    // printf("updateSegmentWithX %d %d %d %d\n", rangeLeft, rangeRight, left, right);    if (left == rangeLeft && rangeRight == right) {        curNode.Y = Y;    } else {        int mid = (left + right)>>1;        if (rangeRight <= mid) {            updateSegmentWithY(pos<<1, rangeLeft, rangeRight, Y);        } else if (rangeLeft > mid) {            updateSegmentWithY(pos<<1|1, rangeLeft, rangeRight, Y);        }    }    pushUp(pos);}void update(int pos, int rangeLeft, int rangeRight, int rotationAngle) {    TreeNode & curNode = tree[pos];    int left = curNode.left;    int right = curNode.right;    // printf("update %d %d %d %d %d\n", rangeLeft, rangeRight, left, right, rotationAngle);    if (rangeLeft <= left && right <= rangeRight) {        curNode.lazyRotation += rotationAngle;        curNode.lazyRotation %= 360;        // printf("Rotate %lf %lf %d\n", curNode.X, curNode.Y,rotationAngle);        Rotate(curNode.X, curNode.Y, getrad(rotationAngle));        // printf("after Rotate %lf %lf %d\n", curNode.X, curNode.Y,rotationAngle);        pushUp(pos);        return;    }    pushDown(pos);    int mid = (left + right)>>1;    if (rangeRight <= mid) {        update(pos<<1, rangeLeft, rangeRight, rotationAngle);    } else if (rangeLeft <= mid && rangeRight > mid) {        update(pos<<1, rangeLeft, mid, rotationAngle);        update(pos<<1|1, mid+1, rangeRight, rotationAngle);    } else if (rangeLeft > mid) {        update(pos<<1|1, rangeLeft, rangeRight, rotationAngle);    }    pushUp(pos);}int main() {    // FILE * in = fopen("/local/POJ/2991In", "r");    while(scanf("%d %d", &segmentsNum, &opNum) != EOF) {        buildTree(1, 1, segmentsNum);        for (int i = 0; i < segmentsNum - 1; i++) {            curDegree[i] = 180; // init all 180, from s -> s+1, counterclock        }        int Y = 0;        for (int i = 1; i <= segmentsNum; i++) {            int length;            scanf("%d", &length);            Y += length;            updateSegmentWithY(1, i, i, length);        }        // printf("begin\n");        for (int i = 0; i < opNum; i++) {            int segmentId;            int newDegree;            scanf("%d %d", &segmentId, &newDegree);            int oldDegree = curDegree[segmentId-1];            if (newDegree != oldDegree) {                update(1, segmentId +1, segmentsNum, newDegree - oldDegree);                curDegree[segmentId-1] = newDegree;            }            printf("%.2lf %.2lf\n", tree[1].X, tree[1].Y);        }        printf("\n");    }    // fclose(in);}

一道几何和线段树结合的题,让已经把几何丢差不多的我痛不欲生,这道题其实能看出大致的思路,但是真正细化到实现的时候却又一头雾水了。

后来搜发现对着道题也是众解纷纭,这里用了一种我觉得最为简便的方法,

一共有N段segment, 并且一开始都是垂直向上立着的,

首先,要把线段树构建起来,

根区间是 [1, N], 表示全部的segments,每个节点都保存这样的信息:

X,Y: 此节点代表的segments的集合的终点相对于起点的坐标

lazyRotation: 此节点代表的segments的集合起点与终点的连线相对于x轴的角度, 因为转动而变化的角度值(也是一个重要的lazy tag, 上面代码里的angle其实没用).

这样要求最后一个segment的绝对坐标,

只需求出 线段树的根区间节点的X,Y即可(根区间的起点是(0,0), 因此X,Y就是相对于(0,0)坐标原点的坐标, 直接就是绝对坐标)。

重要的操作和要点有这几个:

<1> 因为题目的转动输入 是以 将某两个segment i 和 i+1之间的逆时针夹角变化为某个角度来表现的,因为需要搞一个数组,记录当前每个 segment间夹角的值,

这样在有输入时,可以根据此次变化成的角度 D1  和 原来的 segment间夹角 D2来比较。 D1-D2就是此次转动所 实际转动的角度(逆时针方向).

<2> 记住要初始化线段树每个节点的X和Y, 因为初始状态是垂直的,因此,X都为0, 而 Y则是segment的长度:

比如, 有两个segmengt  1 和 2, 长度分别为 10 和 5, 那么线段树共有3个端点,其XY分别为:

                         [1, 2] (X = 0, Y = 15)

           [1 , 1] (X = 0, Y =10)     [2, 2](X = 0, Y = 5)

<2> update 更新线段树的某一段, 因为题目的实际情况, 如果转动第i个segment(注意题的输入是把第i-1和第i个segment之间的夹角变化),其实从第i到最后一个segment N都是被转动的(因为是一个crane,这也是我们可以用lazyTag来进行区间更新的原因), 因此每次更新的区间是(i, N),然后就是标准的线段树更新流程,

如果当前操作的线段树节点区间 被 [i, N]完全覆盖,那么就将这一段进行旋转,更新X 和 Y(直接抄了一个旋转的函数), 以及将 lazyTag加上 此次转动的角度, 然后 pushUp(很重要,因为父区间的X和Y等于其左右孩子区间的X 和 Y的相加,因为我们定义的X和Y是相对的,所以可以这样), 否则 pushDown,检查区间相交情况,然后递归处理,最后,还要pushUp, 一次update只有一次pushUp, 但必须pushUp。

<3> pushUp, 在子区间的X和Y因为旋转变化以后,要将其向上将变化传递到父区间, 父区间的X 和 Y 分别等于 两个子区间的X 和Y的和,

因为我们定义的是相对于这段区间起点的X和Y,所以这个等式成立:

比如, segment1的 起点坐标是 (0, 0) 终点坐标是 (5, 0)(也是相对于(0,0)的相对坐标), 而segment2的起点坐标是 (5,0), 终点坐标是(5,5), 那么 其X , Y也就是(5,5)相对于(5,0)的坐标是 (0,5),

这样, segment1+2组成的区间的终点坐标相对于起点坐标就是 (5,0) + (0, 5) = (5,5)(相对于(0,0)的相对坐标)。可以将X和Y理解为此区间内子区间X和Y的区间和。

<4>pushDown,这一步,就是将lazyTag代表的旋转角度下发到两个子区间去执行,每个子区间分别根据这次的lazyTag的旋转角度旋转自己的X和Y,同时继承累加lazyTag的旋转角度(可以加完 % 一下 360, 这样能避免累加角度过大),最后父区间的laztTag = 0表示没有被lazy的旋转操作。

这道题的精髓就在于 相对坐标的应用 ,可以大大简化题,而旋转则可以体现出线段树的区间批量操作以及lazyTag的高效.

0 0
原创粉丝点击