zoukankan      html  css  js  c++  java
  • POJ 2991 Crane(线段树+计算几何)

    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

    说实话线段树好学,线段树的思想不好学呐,此题:线段树的节点维护的是向量(坐标),偏角

    更新的操作是根据偏角的改变来更改向量的值——数学公式——计算几何

    首先建树:

    void build(int rt,int left,int right)
    {
        //tx,ty坐标
        //angle角度
        angle[rt] = tx[rt] = 0.0;
      //此线段树分为0 2 -- 0 1 --- 1 2
        if(left + 1 == right)
        {
            ty[rt] = L[right] * 1.0;
            return;
        }
        int mid = (left + right) >> 1;
        build(lson);
        build(rson);
      //根据初始的摆放回溯更新y坐标——y向量 ty[rt] = ty[2*rt+1] + ty[2*rt + 2]; }

     偏转更新——1 2 -- 2 3 由 1 3来控制 偏转节点是2 =(1 + 3 ) / 2,0 1 -- 1 2有0 2 控制 控制节点是1,一个小节点的旋转,就要向上更新是的每一个有关的节点旋转

    void update(int rt,int left,int right,int s,double a)
    {
        //cout<<rt<<" "<<left<<" "<<right<<endl;
        if(s > left && s < right)
        {
            //cout<<"继续向前"<<endl;
            int mid = (left + right) >> 1;
            update(lson,s,a);
            update(rson,s,a);
            //因为最后输出的是n节点也就是头节点的坐标也就是向量的值,所以所有的更新都为了线段树的根节点!!
            if(s <= mid)//对上面照成的影响问题//相当于上面的向量都旋转了angle度
                angle[rt] += a;
    
            int sonl = rt * 2 + 1;
            int sonr = rt * 2 + 2;
            //cout<<"past:"<<endl;
            //cout<<rt<<" "<<tx[rt]<<" "<<ty[rt]<<endl;
            double ss = sin(angle[rt]),c = cos(angle[rt]);
            //cout<<ss<<" "<<c<<endl;
            tx[rt] = tx[sonl] + (c * tx[sonr] - ss * ty[sonr]);
            ty[rt] = ty[sonl] + (ss * tx[sonr] + c * ty[sonr]);
            //cout<<"NOW"<<endl;
            //cout<<rt<<" "<<tx[rt]<<" "<<ty[rt]<<endl;
        }
        //cout<<"不在该区间"<<endl;
    }
    

     code......

    #include <cstdio>
    #include <string.h>
    #include <cmath>
    #include <iostream>
    #define lson 2*rt+1,left,mid
    #define rson 2*rt+2,mid,right
    using namespace std;
    const int maxn = (1 << 15) - 1;
    const int maxm = 10001;
    const double PI = acos(-1.0);
    int n,c;
    int L[maxm],s[maxm],alpha[maxm];
    double tx[maxn],ty[maxn];
    double angle[maxn],preangle[maxm];
    
    void init()
    {
        memset(tx,0,sizeof(tx));
        memset(ty,0,sizeof(ty));
        memset(preangle,0,sizeof(preangle));
        memset(angle,0,sizeof(angle));
        for(int i = 0;i <= n;++i)
        {
            preangle[i] = PI;//初始夹角
        }
    }
    void build(int rt,int left,int right)
    {
        //tx,ty坐标
        //angle
        angle[rt] = tx[rt] = 0.0;
    
        if(left + 1 == right)
        {
            ty[rt] = L[right] * 1.0;
            return;
        }
        int mid = (left + right) >> 1;
        build(lson);
        build(rson);
        ty[rt] = ty[2*rt+1] + ty[2*rt + 2];
    }
    void update(int rt,int left,int right,int s,double a)
    {
        //cout<<rt<<" "<<left<<" "<<right<<endl;
        if(s > left && s < right)
        {
            //cout<<"继续向前"<<endl;
            int mid = (left + right) >> 1;
            update(lson,s,a);
            update(rson,s,a);
            //因为最后输出的是n节点也就是头节点的坐标也就是向量的值,所以所有的更新都为了线段树的根节点!!
            if(s <= mid)//对上面照成的影响问题//相当于上面的向量都旋转了angle度
                angle[rt] += a;
    
            int sonl = rt * 2 + 1;
            int sonr = rt * 2 + 2;
            //cout<<"past:"<<endl;
            //cout<<rt<<" "<<tx[rt]<<" "<<ty[rt]<<endl;
            double ss = sin(angle[rt]),c = cos(angle[rt]);
            //cout<<ss<<" "<<c<<endl;
            tx[rt] = tx[sonl] + (c * tx[sonr] - ss * ty[sonr]);
            ty[rt] = ty[sonl] + (ss * tx[sonr] + c * ty[sonr]);
            //cout<<"NOW"<<endl;
            //cout<<rt<<" "<<tx[rt]<<" "<<ty[rt]<<endl;
        }
        //cout<<"不在该区间"<<endl;
    }
    int main()
    {
        int cas = 0;
        while(~scanf("%d%d",&n,&c))
        {
    
            init();
            cas++;
    
            for(int i = 1;i <= n;i++)
                scanf("%d",&L[i]);
            for(int i = 0;i < c;i++)
                scanf("%d%d",&s[i],&alpha[i]);
            build(0,0,n);
            for(int i = 0;i < c;i++)
            {
                //转化为弧度
                double nowalpha = alpha[i] / 180.0 * PI;
                //更新
                update(0,0,n,s[i],nowalpha - preangle[s[i]]);
                preangle[s[i]] = nowalpha;
                //输出节点坐标(向量)
                printf("%.2lf %.2lf
    ",tx[0],ty[0]);
            }
            if(cas != 1)
                printf("
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    C#
    数据库SQL Server
    JavaScript题目
    vscode: Visual Studio Code 常用快捷键
    jQuery教程
    JavaScript快速排序
    JS编程艺术
    JS
    linux 笔记
    积累的各种资源
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/9445117.html
Copyright © 2011-2022 走看看