POJ 2291Crane

描述

有一台起重机,我们把起重机看成由N条线段依次首尾相接而成。第i条线段的长度为Li。最开始,所有的线段都笔直连接,指向上方。
有C条操纵起重机的指令。指令i给出两个整数Si和Ai,效果是使线段Si和Si+1之间的角度变成Ai度。其中角度指的是从线段Si开始延逆时针方向旋转到Si+1所经过的角度。最开始时所有角度都是180度。
按顺序执行这C条指令。在每条指令执行之后,输出起重机的前端(第N条线段的端点)的坐标。假设起重机的指点的坐标是(0,0)

解题报告

以(2,3,4,5,6,7,8,9)为例,建立的线段树如下所示:
POJ 2291Crane
每个节点维持存储:1)序号;2)权值;3)所管理的范围
更新时,维持下面两个值。
1)从第一条线段的起点指向最后一条线段的终点的向量(其为左右儿子节点对应坐标之和)
2)两个儿子节点对应的部分连接之后,右儿子需要转动的角度

向量(x,y)顺时针旋转B度,该向量变成:
x=xcos(B)+ysin(B)
y=ycos(B)-xsin(B)

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;

#define M_PI acos(-1.0)
#define N 10000
#define C 100000
#define ST_SIZE 1<<15

int n,c;
int L[N];//线段长度
int S[C],A[N];//指定要转的线段和角度
double vx[ST_SIZE],vy[ST_SIZE];//各节点的向量
double ang[ST_SIZE];//各节点的角度
double prv[N];//为了查询角度的变化而保存的当前角度的数组

void init(int k, int l, int r)//节点编号,左右区间下标
{
    ang[k]=vx[k]=0.0;
    if (r-l==1)//叶子节点
        vy[k]=L[l];//初始状态下都是竖直向上的
    else//非叶子节点
    {
        int chl=k*2+1, chr=k*2+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 k,int l,int r)//将s和s+1的角度变为a,k是节点编号,lr是左右区间范围
{
    if (s<=l) return;
    else if (s<r)
    {
        int chl=k*2+1,chr=k*2+2;
        int m=(l+r)/2;//中点
        change(s,a,chl,l,m);
        change(s,a,chr,m,r);
        if (s<=m)
        {
            ang[k]+=a;
        }
        double si=sin(ang[k]),co=cos(ang[k]);
        vx[k]=vx[chl]+(co*vx[chr]-si*vy[chr]);
        vy[k]=vy[chl]+(si*vx[chr]+co*vy[chr]);
    }
}
void solve()
{
    init(0,0,n);
    for (int i=1; i<n; i++)
        prv[i]=M_PI;
    for (int i=0; i<c; i++)
    {
        scanf("%d%d",&S[i],&A[i]);
        int s=S[i];
        double a=A[i]/360.0*2*M_PI;//把角度转换成弧度
        change(s,a-prv[s],0,0,n);//a-prv[s]表示要转的角度减去当前的角度
        prv[s]=a;//更新当前的角度
        printf("%.2f %.2f\n", vx[0],vy[0]);
    }
}

int main()
{
    while(cin>>n>>c)//输入数量和查询次数
    {
        for (int i=0; i<n; i++)
            scanf("%d",&L[i]);//输入长度
        solve();
        cout<<endl;
    }
    return 0;
}