起重机有n段,每段都可以旋转,求最后一段的坐标。
http://poj.org/problem?id=2991
线段树。用线段树维护旋转的角度,成段更新。
首先要将每一条线段转换成向量。
根据向量绕原点旋转的表达式,可以计算出旋转后的坐标。
x1 = x0 * cosa - y0 * sina
y1 = x0 * sina + y0 * cosa
x0 = |R| * cosA, y0 = |R| * sinA ------ 1
x1 = |R| * cos(A + B) = |R| * (cosAcosB - sinAsinB)
y1 = |R| * sin(A + B) = |R| * (sinAcosB + cosAsinB)
将1式带入消去cosA和sinA即可
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int maxn = 10000 + 10;
const double eps = 1e-5;
const double pi = acos(-1);
double x[maxn << 2], y[maxn << 2];
int n, c, s, a, cas = 0, d[maxn], ang[maxn << 2]; //ang记录线段树中的旋转角度
void pushup(int rt){
x[rt] = x[rt << 1] + x[rt << 1 | 1];
y[rt] = y[rt << 1] + y[rt << 1 | 1];
}
void Rotate(int rt, int w){ //将rt节点旋转w度
double t = pi * w / 180.0; //将角度转换成弧度
double xx = cos(t) * x[rt] - sin(t) * y[rt];
double yy = sin(t) * x[rt] + cos(t) * y[rt];
x[rt] = xx;
y[rt] = yy;
}
void pushdown(int rt){
Rotate(rt << 1, ang[rt]);
Rotate(rt << 1 | 1, ang[rt]);
ang[rt << 1] += ang[rt];
ang[rt << 1 | 1] += ang[rt];
ang[rt] = 0; //取消节点标记
}
void build(int l ,int r, int rt){ //建立线段树
ang[rt] = 0;
if(l == r){ //是叶子节点
scanf("%lf", &y[rt]);
x[rt] = 0;
return;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
pushup(rt); //向上更新
}
void update(int p, int w, int l, int r, int rt){ //更新线段树
if(p < l){
Rotate(rt, w);
ang[rt] += w;
return;
}
if(ang[rt]) pushdown(rt); //从根节点向下更新
int m = (l + r) >> 1;
if(p < m) update(p, w, lson);
update(p, w, rson); //右儿子一定会旋转
pushup(rt);
}
int main(){
while(~scanf("%d%d", &n, &c)){
if(cas++) puts("");
build(1, n, 1);
for(int i=0; i<=n; i++) d[i] = 180; //初始角度为180
while(c--){
scanf("%d%d", &s, &a);
update(s, a - d[s], 1, n, 1); //逆时针旋转的角度
d[s] = a;
printf("%.2f %.2f\n", fabs(x[1]) < eps ? 0 : x[1], fabs(y[1]) < eps ? 0 : y[1]);
}
}
return 0;
}