POJ2991 Crane(线段树成段更新+向量旋转)

14 篇文章 0 订阅
12 篇文章 0 订阅

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., 180o. 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
这道题呢,涉及的是线段树的成段更新,这里我们更新的每一段的坐标,由于我们求的是最后一段顶点的坐标,所以我们
只需要把所有段的顶点的坐标加起来就是最终的答案。
我们这里用的是向量,那么来了,向量的旋转坐标是怎么变化的呢,这里涉及的是一些数学知识,看下图。

这里写图片描述

所以呢,向量的坐标变化为:
Vx=cosA*x-sinA*y
Vy=cosA*y+sinA*x
接下来就是手写线段树了,就加了一个懒惰标记,成段跟新[q+1,N]这一大段中所有小段的坐标,
q+1,q+2,...,N.由于我们求的是最后一段的点的坐标,所以我们只需要把[1,N]每一段的坐标
加起来就可以了(可能这里会疑问,怎么可以全部加起来呢,那一定坐标变大了,其实,前面说过,
我们更新的每一段的坐标,这些坐标都对应某一段的起点(0,0),旋转后的坐标也是相对的,有正
有负,所以,最后加起来,一定是最顶点的左边,即sum[1]).大概就这些。
ps:这里给的角度是改变后所处的角度,所以我们还需要算出旋转之后的角度。(英语好真的很重要,题都读不懂gg)
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ostreambaba/文档/input.txt", "r", stdin)
#define fout freopen("/home/ostreambaba/文档/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-12;
const int maxn = 10010;
using namespace std;

inline int read(){
    int x(0),op(1);
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')op=-1,ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*op;
}

//Xi=cosx-siny
//Yi=sinx+cosy

double sum[maxn<<2]; //坐标
double re[maxn<<2]; //懒惰标记
double pre[maxn]; //各段当前的角度
double x[maxn<<2]; 
double y[maxn<<2];
double Change(double ang) //角度转弧度
{
    double res=ang/180*Pi;
    return res;
}
inline void work(double ang,int rt) 
{
    if(ang>=360){  //控制在360以内,其实也可以不用
        ang=(int)ang%360;
    }
    double res=Change(ang);
    double vx=x[rt];
    double vy=y[rt];
    x[rt]=cos(res)*vx-sin(res)*vy; //向量旋转的坐标变化
    y[rt]=cos(res)*vy+sin(res)*vx;  //
}
inline void PushUp(int rt)
{
    x[rt]=x[rt<<1]+x[rt<<1|1];
    y[rt]=y[rt<<1]+y[rt<<1|1];
}
inline void PushDown(int rt)
{
    if(re[rt]){
        work(re[rt],rt<<1);
        work(re[rt],rt<<1|1);
        re[rt<<1]+=re[rt];
        re[rt<<1|1]+=re[rt];
        re[rt]=0;
    }
}
inline void buildTree(int l,int r,int rt)
{
    re[rt]=0,x[rt]=0; //初始化
    if(l==r){
        scanf("%lf",&y[rt]);
        return;
    }
    int m=(l+r)>>1;
    buildTree(lson);
    buildTree(rson);
    PushUp(rt);
}
inline void update(double ang,int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R){
        re[rt]+=ang;
        work(ang,rt); 
        return;
    }
    PushDown(rt);
    int m=(l+r)>>1;
    if(L<=m){
        update(ang,L,R,lson);
    }
    if(R>m){
        update(ang,L,R,rson);
    }
    PushUp(rt);
}
int main()
{
    //fin;
    int N,C;
    double ang,tmp;
    int lenCount;
    bool flag=false;
    while(~scanf("%d%d",&N,&C)){
        if(flag){
            cout<<endl;
        }
        flag=true;
        buildTree(1,N,1);
        for(int i=1;i<=N;++i){ //初始状态,180度
            pre[i]=180;
        }
        while(C--){
            scanf("%d%lf",&lenCount,&ang);
            tmp=ang-pre[lenCount+1]; //算出旋转的角度,注意从逆时针开始
            pre[lenCount+1]=ang;
            update(tmp,lenCount+1,N,1,N,1);
            printf("%.2f %.2f\n",x[1],y[1]);//这里怎么说,%.2lf g++超时,c++就不会,而%.2f就没事
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值