题意:有一个挖掘机的手臂,它有很多节,开始的时候都是垂直于地面的,然后要对手臂的关节部分进行旋转,旋转到:从逆时针开始的角度a。每一次旋转后,都需要输出最后一节的尾部的坐标。
每次旋转的时候,需要用表达式来来表达,这个表达式选择的是向量,向量的旋转还要表达出来。没有学好向量,看到这题很懵逼。向量的旋转和复数的运算有关。
两个复数:z1=x1+y1*i=r1*(cosa1+sina1*i),z2=x2+y2*i=r2*(cosa2+sina2*i)
z1*z2=r1*r2(cosa1*cosa2+cosa1*sina2*i+sina1*i*cosa2-sina1*sina2)
=r1*r2(cosa1*cosa2-sina1*sina2+(cosa1*sina2+sina1*cosa2)*i)
//三角变换,cos(a+b)=cosa*cosb-sina*sinb,sin(a+b)=sina*cosb+cosa*sinb
=r1*r2(cos(a1+a2)+sin(a1+a2)*i)
由此可以看出两个复数相乘等于模相乘,角度相加。
所以对于一个向量z=(x,y)=x+y*i,把它和一个模为1的向量cosa+sina*i相乘
就相当于把它加上了角度a,也就是逆时针旋转了a度
z'=x*cosa+x*sina*i+y*i*cosa-y*sina
=x*cosa-y*sina+(x*sina+y*cosa)*i
z'=(x*cosa-y*sina,x*sina+y*cosa)
即x'=x*cosa-y*sina,y'=x*sina+y*cosa;
然后还需要一个prv数组来保存原来的角度,以便算出旋转了多少。
之后的线段树操作就没有什么特别了。
值得注意的是:if (s <= mid)ang[k] += a;,只有当s<=mid的时候角度才能加上a,因为是先修改的左儿子和右儿子的值,
当s>mid的时候已经改变了右儿子的值,而这个题是修改s和s+1之间的角度,如果s在mid右边,改变了右边的值就不需要再修改了,修改右边的值对左边的产生不了影响。
然而修改左边的值,只是改变了左边的值,但是实际上右边的值依然需要修改,因为要求的是最右边的端点的坐标。
#pragma warning(disable:4996)
#include <cstring>
#include<cstdio>
#include <algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<climits>
using namespace std;
typedef long long ll;
const int maxn = 20005;
//数组开大点,不然RE
double vx[maxn<<2], vy[maxn<<2];
const double PI =acos(-1.0);
double prv[maxn<<2], ang[maxn<<2];
int A[maxn<<2], S[maxn<<2];
int L[maxn<<2];
//建树,初始化每个结点坐标的x和y值
void init(int k, int l, int r)
{
ang[k] = 0;vx[k] = 0;
if (r == l)vy[k] = L[l];
else
{
int mid = (l + r) >> 1;
init(2 * k, l, mid);
init(2 * k + 1, mid + 1, r);
vy[k] = vy[2 * k] + vy[2 * k + 1];
}
}
void change(int s, double a, int k, int l, int r)
{
if (s<l || s>=r)return;
int mid = (l + r) / 2;
change(s, a, 2 * k, l, mid);
change(s, a, 2 * k + 1, mid+1, r);
if (s <= mid)ang[k] += a;//一定要判断
double ss = sin(ang[k]), cc = cos(ang[k]);
//左边的加上右边旋转后的
vx[k] = vx[2 * k ] + (vx[2 * k +1] * cc - vy[2 * k + 1] * ss);
vy[k] = vy[2 * k ] + (vx[2 * k +1] * ss + vy[2 * k + 1] * cc);
}
int main()
{
int N, C, i, j, cnt = 0;
while (scanf("%d%d", &N, &C) == 2)
{
if (cnt != 0)
{
printf("\n");
}
cnt++;
for (i = 1;i <= N;i++)
scanf("%d", &L[i]);
for (i = 1;i <= C;i++)
{
scanf("%d%d", &S[i], &A[i]);
}
init(1,1,N);
for (i = 1;i < N;i++)
{
prv[i] = PI;//原来都是竖直的,角度为180度
}
for (i = 1;i <= C;i++)
{
int s = S[i];
double a = A[i] / 180.0 * PI;//转换为弧度制
change(s, a - prv[s], 1, 1, N);
prv[s] = a;
printf("%.2lf %.2lf\n", vx[1], vy[1]);
}
}
return 0;
}