POJ 2991-Crane(线段树-角度旋转)

Crane
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 5333   Accepted: 1441   Special Judge

Description

ACM has bought a new crane (crane -- jeřáb) . The crane consists of n segments of various lengths, connected by flexible joints. The end of the i-th segment is joined to the beginning of the i + 1-th one, for 1 ≤ i < n. The beginning of the first segment is fixed at point with coordinates (0, 0) and its end at point with coordinates (0, w), where w is the length of the first segment. All of the segments lie always in one plane, and the joints allow arbitrary rotation in that plane. After series of unpleasant accidents, it was decided that software that controls the crane must contain a piece of code that constantly checks the position of the end of crane, and stops the crane if a collision should happen. 

Your task is to write a part of this software that determines the position of the end of the n-th segment after each command. The state of the crane is determined by the angles between consecutive segments. Initially, all of the angles are straight, i.e., 180 o. The operator issues commands that change the angle in exactly one joint. 

Input

The input consists of several instances, separated by single empty lines. 

The first line of each instance consists of two integers 1 ≤ n ≤10 000 and c 0 separated by a single space -- the number of segments of the crane and the number of commands. The second line consists of n integers l1,..., ln (1 li 100) separated by single spaces. The length of the i-th segment of the crane is li. The following c lines specify the commands of the operator. Each line describing the command consists of two integers s and a (1 ≤ s < n, 0 ≤ a ≤ 359) separated by a single space -- the order to change the angle between the s-th and the s + 1-th segment to a degrees (the angle is measured counterclockwise from the s-th to the s + 1-th segment).

Output

The output for each instance consists of c lines. The i-th of the lines consists of two rational numbers x and y separated by a single space -- the coordinates of the end of the n-th segment after the i-th command, rounded to two digits after the decimal point. 

The outputs for each two consecutive instances must be separated by a single empty line.

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

Source

CTU Open 2005

题目意思:

可以抽象如下:
有N条线段,初始状态全部竖直向上,在每条线段的最上方有一个可以旋转的关节点。
当一个关节点旋转时,它上面所有的线段都会跟着旋转。
每次旋转后,求最上面一个关节点的坐标。

解题思路:

线段树区间更新。 
结点值保存该区间的向量及旋转角(注意他给出的不是旋转角)一个区间的向量值=左子区间的向量+右子区间的向量。 
求一个向量(x0,y0)逆时针旋转B度后的向量有一个公式: 
x1= x0 * cosB - y0 * sinB 
y1 = x0 * sinB + y0 * cosB 
顺时针就把-B代入: 
x1= x0 * cosB + y0 * sinB 
y1 = -x0 * sinB + y0 * cosB

#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)
        {
            cout<<s<<" "<<m<<" "<<l<<" "<<r<<" "<<k<<endl;
            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;//最开始所有角度都是180度
    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;
}


你可能感兴趣的:(C++,线段树,poj,2991,Crane,角度旋转)