Crane(POJ-2991)(线段树)

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

这到题是昨天开始研究,最后是说用线段树,给我了一份代码,这个代码,昨天研究了一天,没有结果,心情烦躁,离开实验时也是念念不忘,可能是当时那种状态,就没办法想出来,今天又来到实验室,竟然10分钟就想明白了,真是人生。。。感谢一位大师兄对我说的话,人就是应该放下执念,但是有时候就是不愿意放手,得改一改了,自己还是得多多学习,路还有很长。
需要的的数学知识是 :一个坐标,(向量比较合适)绕远点点旋转,规定逆时针旋转为真方向,即旋转角为正值,即有:
**设向量为(x,y)旋转之后的点为(x’,y’),设旋转角为α
x’=x*cosα-y*sinα
y’=x*sinα+y*cosα**

代码:

import java.util.Arrays;
import java.util.Scanner;



public class Main 
{
    static double x[],y[],ang[];//每个信息节点用来维护三个量,向量(x,y)以及他的左右子线段中两个向量之间相对需要转的角度
    static Scanner sc=new Scanner(System.in);
    static double pre[];//用来维护每个操作状态以后线和线的绝对角度
    public static void main(String[]args)
    {
        while(sc.hasNext())
        {
            int n=sc.nextInt();
            int t=sc.nextInt();
            x=new double[n<<2];
            y=new double[n<<2];
            ang=new double[n<<2];
            pre=new double[n+1];
            Arrays.fill(pre,Math.PI);//题设要求,每个角度都是180,这里都用弧度数表示,计算需要
            buildUp(1,1,n);
            while((t--)>0)
            {
                int pos=sc.nextInt();//位置信息
                double a=sc.nextInt()/180.0*Math.PI;//把输入的角度转化成弧度数
                change(pos,a-pre[pos],1,1,n);//两个绝对角度相减即得到相对角度
                pre[pos]=a;//更新这个点的角度
                System.out.printf("%.2f %.2f\n",x[1],y[1]);
            }
            System.out.println();
        }
    }
    static void buildUp(int s,int L,int R)
    {
        x[s]=0;
        if(L==R)
        {
            y[s]=sc.nextInt();//一个小小技巧,之前有说过
            return;
        }
        int m=(L+R)>>1;
        buildUp(s<<1,L,m);
        buildUp(s<<1|1,m+1,R);
        y[s]=y[s<<1]+y[s<<1|1];//pushup向上更新
    }
    static void change(int pos,double r,int s,int L,int R)
    {
        if(L<=pos&&pos<R)//如果要改变的点在这个线段里,因为角度维护的是两个子线段的关系。如果这个点在这个区间之外,那么这个更新操作一定是他的父亲或父亲以上节点来做,自己本身是不需要变化的
        {
            int m=(L+R)>>1;
            change(pos,r,s<<1,L,m);
            change(pos,r,s<<1|1,m+1,R);
            if(pos<=m)ang[s]+=r;//相对角度一直都是在累加的,当pos这个点在他的左孩子,那么右孩子的所有的线段都需要转角度,如果pos在右孩子,那么这个转角度已经被pos操作过,所以之身就不需要累加了
            double sin=Math.sin(ang[s]),cos=Math.cos(ang[s]);
            x[s]=x[s<<1]+(cos*x[s<<1|1]-sin*y[s<<1|1]);
            y[s]=y[s<<1]+(sin*x[s<<1|1]+cos*y[s<<1|1]);//因为最终,左区间是相对不动的,是右孩子相对于左孩子的角度变化,所以对右孩子操作,进行转角(但是这个转角是对父亲来说的,这个变化是体现在父亲节点的,右孩子的节点信息并没有发生实质的修改)依次来更新此节点,即父亲节点的向量值
        }
    }

}

其实我觉得我说的不是很清楚,以后会慢慢改注释
peace&love

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值