poj-2991

13 篇文章 0 订阅
    // 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值