Description
有一台起重机。我们把起重机看作由N条线段依次首尾相接而成。第i条线段的长度是Li。最开始,所有的线段都笔直连接,指向上方。现有C条操纵起重机的指令。指令i给出两个整数Si和Ai,效果是使线段Si和Si+1之间的角度变成Ao度。其中角度指的是从线段Si开始沿逆时针方向旋转到Si+1所经过的角度。最开始所有角度都是180度。按顺序执行这C条指令。在每条指令执行之后,输出起重机的前段(第N条线段的端点)的坐标。假设起重机的支点坐标是(0,0)
Input
多组用例,每组用例第一行为两个整数N和C分别表示起重机的线段数和指令数,第二行为N个整数表示各线段的长度,之后C行每行两个整数表示指令,以文件尾结束输入
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
Solution
本题用线段树来解决,每个节点表示一段连续的线段的集合,并且维护下面两个值
1,把对应的线段集合中的第一条线段转至垂直方向后,从第一条线段的起点指向最后一条线段的终点的向量
2,(如果该节点有叶子节点)两个儿子节点对应的部分连接后,右儿子需要转动的角度
也就是说,如果节点i表示的向量是Xi,Yi,角度是angi,两个儿子节点是chl和chr,那么就有
Xi=Xchl+(cos(angi)*Xchr-sin(angi)*Ychr)
Yi=Ychl+(sin(angi)*Xchr+cos(angi)*Ychr)
这样,每次更新便在O(logn)时间内完成,而输出的值就是根节点对应向量的值
Code
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
#define maxn 10001
const int ST_SIZE=(1<<15)-1;
int N,C;
int L[maxn];
int S[maxn],A[maxn];
//线段树所维护的数据
double vx[ST_SIZE],vy[ST_SIZE];//各节点的向量
double ang[ST_SIZE];//各节点的角度
double prv[maxn];//为了查询角度的变化而保存当前角度的数组
void init(int k,int l,int r)//初始化线段树,k为节点编号,l,r表示当前节点对应的是[l,r)区间
{
ang[k]=vx[k]=0.0;//初始状态x坐标和角度均为0
if(r-1==l)//叶子节点
vy[k]=L[l];
else//非叶子节点
{
int chl=2*k+1,chr=2*k+2;
init(chl,l,(l+r)/2);
init(chr,(l+r)/2,r);
vy[k]=vy[chl]+vy[chr];
}
}
void change(int s,double a,int v,int l,int r)//把s和s+1的角度变成a,v是节点编号,l,r表示当前节点对应的是[l,r)区间
{
if(s<=l)//s不在该区间
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]);
}
}
int main()
{
int res=0;
const double PI=acos((double)-1);
while(scanf("%d%d",&N,&C)!=EOF)
{
res++;
if(res!=1)//相邻两组输出间用空行隔开
printf("\n");
memset(vx,0,sizeof(vx));//初始化
memset(vy,0,sizeof(vy));
memset(prv,0,sizeof(prv));
memset(ang,0,sizeof(ang));
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]);
init(0,0,N);//初始化
for(int i=1;i<N;i++)
prv[i]=PI;
for(int i=0;i<C;i++)//执行指令
{
int s=S[i];
double a=A[i]/360.0*2*PI;//把角度换算成弧度
change(s,a-prv[s],0,0,N);
prv[s]=a;
printf("%.2lf %.2lf\n",vx[0],vy[0]);
}
}
return 0;
}