POJ 2991 Crane(线段树+计算几何)

85 篇文章 0 订阅
59 篇文章 0 订阅

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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值