题目链接:
题目大意:
有一个吊车臂,有相连的
n
段,每两段相连的地方可以旋转。初始吊车臂是竖直的,给出
在每个操作后,输出最顶端那个点的坐标。
数据范围:
1≤n≤10000C…
没看到
1≤li≤1001≤s<n0≤θ≤359
解题思路:
一般像这种求坐标的题,为了保证精度都会用向量,这道题也是的。
先前我只知道在直角坐标系中,从原点开始的向量和坐标是一一对应的关系,在复平面内,一个从原点开始的向量和复数是一一对应的关系。做了这道题之后,才知道坐标、向量、复数 这三者都存在一一对应的关系。
即点
Z(a,b)
,复数
z=a+bi
,向量
OZ→
存在下图的对应关系:
回到正题,将每一段用向量表示(由起点到终点),最后顶点的坐标就为所有小段的向量和。(这个想想都应该知道,首尾相接)
对于这道题而言,某一操作”
sθ
“,旋转之后,
s
后面的那些线段的相对关系是不会变的,其实就相当于后面的所有线段都旋转了
最终是要得到坐标,所以要通过旋转的角度来求出旋转后的坐标。
一个复数有多种表示形式,如:
z=a+bi=Reiφ=R(cosφ+isinφ)
(其中
R
表示复数
欧拉公式:
eiφ=cosφ+isinφ
如下图,复平面上一个向量
OZ→=(x,y)
,其中
x=Rcosφ,y=Rsinφ
逆时针旋转
θ
之后,
OZ′→=Rei(φ+θ)=R(cos(φ+θ)+isin(φ+θ))
,好!来!公式推起!
⇒OZ′→=R(cosφcosθ−sinφsinθ+i(sinφcosθ+cosφsinθ))
⇒OZ′→=Rcosφcosθ−Rsinφsinθ+i(Rsinφcosθ+Rcosφsinθ)
其中
x=Rcosφ,y=Rsinφ
⇒OZ′→=xcosθ−ysinθ+i(ycosθ+xsinθ)
所以,
x′=xcosθ−ysinθy′=ycosθ+xsinθ
顺时针的话就把
OZ′→=Rei(φ−θ)推出来就好了。
公式推出来了,接下来就交给线段树了,详见代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 10010;
const double PI = 3.1415926535897932384626;
int n, C;
struct segtree {
int l, r;
double x, y; //当前节点所管辖区间的向量
}tree[4 * MaxN + 5];
int lazy[4 * MaxN + 5]; //当前节点所管辖区间 要旋转的角度
int angle[MaxN + 5]; //angle[i]表示节点i和(i + 1)当前的夹角,依照题意,逆时针
double Len[MaxN + 5]; //各线段长度
//其实就是区间修改,只是为了贴合题意,取名叫rotate
void rotate(int rt, int val) {
double Theta = (double)val * PI / 180.0; //转化为弧度制
//向量旋转Theta弧度后,求新坐标的公式
double x = tree[rt].x * cos(Theta) - tree[rt].y * sin(Theta);
double y = tree[rt].x * sin(Theta) + tree[rt].y * cos(Theta);
tree[rt].x = x;
tree[rt].y = y;
}
void push_up(int rt) {
tree[rt].x = tree[rt << 1].x + tree[rt << 1 | 1].x;
tree[rt].y = tree[rt << 1].y + tree[rt << 1 | 1].y;
}
void push_down(int rt) {
if(lazy[rt] != 0) { //保存的是旋转的角度,所以有可能是负的!!!挂了不知多少发!!!
lazy[rt << 1] += lazy[rt];
lazy[rt << 1 | 1] += lazy[rt];
rotate(rt << 1, lazy[rt]);
rotate(rt << 1 | 1, lazy[rt]);
lazy[rt] = 0;
}
}
void Build(int rt, int l, int r) { //建树
tree[rt].l = l; tree[rt].r = r;
lazy[rt] = 0;
if(l == r) {
tree[rt].x = 0.0;
tree[rt].y = Len[l];
return;
}
int mid = (l + r) >> 1;
Build(rt << 1, l, mid);
Build(rt << 1 | 1, mid + 1, r);
push_up(rt);
}
void update(int rt, int L, int R, int val) {
if(L <= tree[rt].l && tree[rt].r <= R) {
lazy[rt] += val;
rotate(rt, val);
return;
}
push_down(rt);
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(L <= mid) update(rt << 1, L, R, val);
if(R > mid) update(rt << 1 | 1, L, R, val);
push_up(rt);
}
int main()
{
while(scanf("%d %d", &n, &C) != EOF)
{
for(int i = 1; i <= n; i++) scanf("%lf", &Len[i]);
Build(1, 1, n);
for(int i = 1; i <= n; i++) angle[i] = 180; //初始为180
for(int i = 1; i <= C; i++) {
int a, b;
scanf("%d %d", &a, &b);
update(1, a + 1, n, b - angle[a]);
angle[a] = b;
printf("%.2lf %.2lf\n", tree[1].x, tree[1].y);
//最后答案显然就是根节点的向量
}
printf("\n");
}
return 0;
}