Time Limit: 2000MS | Memory Limit: 65536K | |||
Total Submissions: 5091 | Accepted: 1368 | Special Judge |
Description
Input
Output
Sample Input
2 1 10 5 1 90 3 2 5 5 5 1 270 2 90
Sample Output
5.00 10.00 -10.00 5.00 -5.00 10.00
题意:
有一台起重机。我们把起重机看成由n条线段依次收尾相接而成。第i条线段的长度是Li。最开始,所有的线段都笔直连接,指向上方。
有c条操纵起重机的指令。指令i给出两个整数Si和Ai,效果是使线段Si和Si+1之间的角度变成Ai度。其中角度指的是从线段Si开始沿逆时针方向旋转到Si+1所经过的角度。最开始时所有的角度都是180度。
按顺序执行这c条指令。在每条指令执行之后,输出起重机的前端(第n条线段的端点)的坐标。假设起重机的支点的坐标是(0, 0)。
分析:
本题可以使用线段树来解决。每个节点表示一段连续的线段的集合,并且维护下面两个值。
*把对应的线段集合中的第一条线段转至垂直方向之后,从第一条线段的起点指向最后一条线段的终点的向量。
*(如果该节点有儿子节点)两个儿子节点对应的部分连接之后,右儿子需要转动的角度。也就是说,如果节点i表示的向量是vxi,vyi,角度是angi,两个儿子节点是chl和chr,那么就有
VXi = VXchl + (cos(angi) * VXchr –sin(angi) * VYchr)
VYi = VYchl + (sin(angi) * VXchr + cos(angi)* Vychr)
这样,每次更新便可在O(logn)时间内完成,而输出的值就是根节点对应的向量的值。
下面的实现和RMQ的有所不同,线段树的大小并没有扩大2的幂。这个时候,线段树并不是一颗完美二叉树,但在本题中同样可以完成各种操作。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #define M_PI acos(-1.0) using namespace std; const int ST_SIZE = (1 << 15) - 1; const int maxn = 10000 + 10; int N, C; int L[maxn], S[maxn], A[maxn]; //线段树所维护的数据 double vx[ST_SIZE], vy[ST_SIZE]; //各节点的向量 double ang[ST_SIZE]; //各节点的角度 //为了查询角度的变化而保存的当前角度的数组 double prv[maxn]; //初始化线段树 //k是节点的编号,l, r表示当前节点对应的是[l, r)区间 void init(int k, int l, int r) { ang[k] = vx[k] = 0.0; if (r - l == 1){ //叶子节点 vy[k] = L[l]; } else{ //非叶子节点 int chl = k * 2 + 1, chr = k * 2 + 2; init(chl, l, (l + r) / 2); init(chr, (l + r) / 2, r); vy[k] = vy[chl] + vy[chr]; } } //把s和s + 1的角度变成a //v是节点的编号,l, r表示当前节点对应的是[l, r)区间 void change(int s, double a, int v, int l, int r) { if (s <= l) return; else if (s < r){ int chl = v * 2 + 1, chr = v * 2 + 2; int m = (l + r) / 2; change(s, a, chl, l, m); change(s, a, chr, m, r); if (s <= m) ang[v] += a; double s = sin(ang[v]), c = cos(ang[v]); vx[v] = vx[chl] + (c * vx[chr] - s * vy[chr]); vy[v] = vy[chl] + (s * vx[chr] + c * vy[chr]); } } void solve() { //初始化 init(0, 0, N); for (int i = 1; i < N; i++) prv[i] = M_PI; //处理操作 for (int i = 0; i < C; i++){ int s = S[i]; double a = A[i] / 360.0 * 2 * M_PI; //把角度换算为弧度 change(s, a - prv[s], 0, 0, N); prv[s] = a; printf("%.2f %.2f\n", vx[0], vy[0]); } } int main() { while (scanf("%d%d", &N, &C) != EOF){ memset(L, 0, sizeof(L)); memset(S, 0, sizeof(S)); memset(A, 0, sizeof(A)); for (int i = 0; i < N; i++){ scanf("%d", &L[i]); } for (int i = 0; i < C; i++){ scanf("%d%d", &S[i], &A[i]); } solve(); } return 0; }