// 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的高效.