Advanced Data Structures :: Segment Tree
Description
有一个机械臂,想象它以直角坐标系的原点为轴,一节连着一节,最末端为机械手。
你可以旋转任意一个关节,改变机械手的位置。
(有点像《cut the rope》中的机械臂?没错,就是那么个玩意)
而现在要做的就正是这么一件事。
告诉你机械臂每节手臂的长度,不停得去操作它(旋转某个关节)。
请求出每次操作之后机械手的位置。
Type
Advanced Data Structures :: Segment Tree
Analysis
刚开始看可能会质疑,很奇怪,这题和线段树有很什么关系。
其实这题是计算几何和线段树的合体。
我们把手臂都各自看成一个向量。
则机械手的位置正好是手臂向量之和。
旋转某个关节,其实就是把关节到机械手之间的手臂向量统统旋转。
由于手臂很多,要每个向量做相同的旋转操作很费时间。
这时候我们就可以想到用线段树的优势正是可以快速地成段更新。
其实和一般的成段更新题目没什么差别,只是我们通常只是成段替换、或者成段增加。
这时候要做的是,对向量成段得做旋转变换。
只要利用旋转变化矩阵即可。
要注意,从第 s 节手臂开始到机械手要旋转的角度,和第 s - 1 节手臂的角度有关。
因此我们还要记录每个手臂的角度,并且去Query得到它们,才能知道我们要旋转的角度。
Solution
#include <cstdio>
#include <cmath>
#define LSon(x) ((x) << 1)
#define RSon(x) ((x) << 1 | 1)
const int MAXN = 10002;
const int ROOT = 1;
const double PI = acos(-1.0);
const double EPS = 1e-8;
struct Seg {
double x, y;
int flag;
int degree;
};
void Rotate(Seg& node, int degree);
struct SegTree {
Seg node[MAXN << 2];
void Update(int pos) {
node[pos].x = node[LSon(pos)].x + node[RSon(pos)].x;
node[pos].y = node[LSon(pos)].y + node[RSon(pos)].y;
}
void Build(int l, int r, int pos) {
node[pos].x = node[pos].y = 0;
node[pos].flag = 0;
node[pos].degree = 0;
if (l == r) {
scanf("%lf", &node[pos].y);
return;
}
int m = l + r >> 1;
Build(l, m, LSon(pos));
Build(m + 1, r, RSon(pos));
Update(pos);
}
void Push(int pos) {
Seg& father = node[pos];
Seg& lson = node[LSon(pos)];
Seg& rson = node[RSon(pos)];
if (father.flag) {
Rotate(lson, father.flag);
Rotate(rson, father.flag);
lson.flag += father.flag;
rson.flag += father.flag;
father.flag = 0;
}
}
void Modify(int l, int r, int pos, int x, int y, int z) {
if (x <= l && r <= y) {
Rotate(node[pos], z);
node[pos].flag += z;
return;
}
Push(pos);
int m = l + r >> 1;
if (x <= m) Modify(l, m, LSon(pos), x, y, z);
if (y > m) Modify(m + 1, r, RSon(pos), x, y, z);
Update(pos);
}
int Query(int l, int r, int pos, int x) {
if (l == r) return node[pos].degree;
Push(pos);
int m = l + r >> 1;
if (x <= m) return Query(l, m, LSon(pos), x);
else return Query(m + 1, r, RSon(pos), x);
}
};
int n, c;
int s, a;
SegTree tree;
double GetRad(int x);
int main() {
bool first = true;
while (scanf("%d%d", &n, &c) != EOF) {
tree.Build(0, n - 1, ROOT);
printf("%s", first ? first = false, "" : "\n");
for (int i = 0; i < c; i++) {
scanf("%d%d", &s, &a);
int degree = tree.Query(0, n - 1, ROOT, s - 1) + 180 + a -
tree.Query(0, n - 1, ROOT, s);
tree.Modify(0, n - 1, ROOT, s, n - 1, degree);
printf("%.2lf %.2lf\n", tree.node[ROOT].x + EPS, tree.node[ROOT].y + EPS);
}
}
return 0;
}
double GetRad(int x) {
return x * PI / 180;
}
void Rotate(Seg& node, int degree) {
double rad = GetRad(degree);
double x = node.x; double y = node.y;
node.x = x * cos(rad) + y * -sin(rad);
node.y = x * sin(rad) + y * cos(rad);
node.degree = (node.degree + degree) % 360;
}