题目链接:http://poj.org/problem?id=2991
题意:有一个活动的机械臂,这个机械臂由多个关节组成,每个关节都可以活动。现在将关节的角度进行调整,求出机械臂顶部的坐标。。
解题思路:这道题目我思考了很长时间,就是想不到怎么做。后来,去网上搜索了很多的题解,网上的题解大都是用线段树做的。刚开始,我也很疑惑,为什么题目可以用线段树解决?后来才发现,动了前面的关节之后,后面关节的相对的夹角是不改变的。这样的话,把每段线段看成一个向量,每次旋转都可以用线段树进行维护。线段树我也只是刚刚入门。我只是学习了点更新,区间更新,我还不甚熟练,长了好长时间才解决。做个题目,首先需要掌握一个公式:向量旋转a弧度之后的公式(cos(a)*x-sin(a)*y,sin(a)*x+cos(a)*y)。然后,每次旋转都用add数组来保存转过的角度。然后,依次向上更行坐标。
#include <cstdio>
#include <cstring>
#include <cmath>
#define PI acos(-1.0)
#define eps 1e-9
const int MAX = 10010;
struct point
{
double x,y;
}p[MAX<<2];
int add[MAX<<2];
int angle[MAX];
void push_up(int rt)
{
p[rt].x = p[rt<<1].x+p[rt<<1|1].x;
p[rt].y = p[rt<<1].y+p[rt<<1|1].y;
}
void rolate(int a,int rt)
{
double x = p[rt].x;
double y = p[rt].y;
double ang = PI/180*a;
p[rt].x = cos(ang)*x-sin(ang)*y;
p[rt].y = sin(ang)*x+cos(ang)*y;
}
void push_down(int rt)
{
if(add[rt])
{
rolate(add[rt],rt<<1);
rolate(add[rt],rt<<1|1);
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
add[rt] = 0;
}
}
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%lf",&p[rt].y);
p[rt].x = 0;
return;
}
int mid = l+r>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
push_up(rt);
}
void update(int a,int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
{
add[rt]+=a;
rolate(a,rt);
return;
}
push_down(rt);
int mid = r+l>>1;
if(L<=mid)
update(a,L,R,l,mid,rt<<1);
if(R>mid)
update(a,L,R,mid+1,r,rt<<1|1);
push_up(rt);
}
int main()
{
int n,m;
int cnt = 0;
while(scanf("%d%d",&n,&m)==2)
{
if(cnt++) puts("");
build(1,n,1);
for(int i=1; i<=n; i++)
angle[i] = 180;
memset(add,0,sizeof(add));
while(m--)
{
int s,a;
scanf("%d%d",&s,&a);
int x = (a-angle[s]+360)%360;
update(x,s+1,n,1,n,1);
angle[s]=a;
printf("%.2lf %.2lf\n",p[1].x+eps,p[1].y+eps);
}
}
return 0;
}