zoukankan      html  css  js  c++  java
  • POJ 2991–Crane【线段树+几何】

    题意:

    把手臂都各自看成一个向量,则机械手的位置正好是手臂向量之和。旋转某个关节,其实就是把关节到机械手之间的手臂向量统统旋转。

    由于手臂很多,要每个向量做相同的旋转操作很费时间。这时就可以想到用线段树的优势正是可以快速地成段更新。和一般的成段更新题目没什么差别,只是通常成段替换、或者成段增加。这时候要做的是,对向量成段得做旋转变换。只要利用旋转变化矩阵即可。

    要注意,从第 s 节手臂开始到机械手要旋转的角度,和第 s - 1 节手臂的角度有关。因此我们还要记录每个手臂的角度,并且去Query得到它们,才能知道我们要旋转的角度。

    思路:

    如果我们将其中某一个线段旋转β角,那么这个线段上方的所有线段都会旋转β角,这就很类似线段树中的对区间加上一个常数的问题了,于是不妨向着线段树的思路去想。

    接下来一个问题就是β角是相对于谁的,换句话说我们所谓的每个线段都会旋转β角,那么是绕谁旋转的?实际上,如果我们局限于把线段当线段看的话,那么这个旋转就会看成是绕某个定点的,这个点就是我们旋转的线段和它下面那个不动的线段的交点,再这样想下去我们就没法处理了,因为每个旋转操作所绕的定点不是唯一的,我们没办法把所有的旋转操作都统一到一起,那么我们就没办法把旋转操作叠加,这样就没法使用线段树了。

    但如果换个思路的话,实际上β角还等于这个线段旋转后所在的直线和未旋转前所在的直线的夹角,而直线的夹角是可以用向量的夹角表示的,如果我们把线段看成一个向量的话那么β角就是这个向量旋转的角度。如果这么看的话,所有的旋转操作就可以统一到一起了,也可以叠加了,因为这样不会局限于绕哪个定点,只需要把向量自身旋转一下就OK。其实说到这里,我们会发现其实上一段的分析从一开始就误入歧途了,我们着眼于了“β角是相对于谁的”,因为相对的东西是不可能统一到一起的,参考系不一样结果就不一样,所以我们要想把每个线段的旋转操作统一到一起,就要去看这些旋转操作改变了哪些绝对的量,而向量就是一个绝对的量,它并不参考与其他的东西,只由这个线段自身的状态决定。

    又扯远了,还是分析下怎么实现吧。前面说了,如果把线段看成向量的话,我们每次的旋转操作就可以看成是对区间上的所有点加上一个值(向量的旋转角),那么剩下的问题有两个:第一,也是最重要的,我们这样记录能不能每次方便地求出终点的位置?第二,题目中给出的是每次设定的两个线段的夹角,我们能不能方便的将其转化成相对于以前的状态旋转了多少角度?

    对于第一个问题,我们既然有了每个线段的向量,那么我们只要把这些向量求和,最终所指的位置就是终点,因此我们只要维护好向量的区间和就可以了。对于第二个问题,我们可以用一个数组degree[i]表示第i个向量和第i-1一个向量当前的夹角,这样就有了当前的状态,每次读入操作后就会方便的得到相当于进行旋转多少角度的操作了,然后再更新一下degree[i]即可。并且我们每读入一个操作只会影响一个degree[]的值,不会影响到其他的degree[]。

    总而言之,我们要把每个线段看成一个向量,并维护这些向量的区间和,同时要实现对区间中每个元素都加上一个值这一操作。

    #include <cstdio>
    #include <cmath>
    
    #define LSon(x) ((x) << 1)
    #define RSon(x) ((x) << 1 | 1)
    
    const int MAXN = 10002;
    const int ROOT = 1;
    const double PI = acos(-1.0);
    const double EPS = 1e-8;
    
    struct Seg {
        double x, y;
        int flag;
        int degree;
    };
    
    void Rotate(Seg& node, int degree);
    
    struct SegTree {
        Seg node[MAXN << 2];
        void Update(int pos) {
            node[pos].x = node[LSon(pos)].x + node[RSon(pos)].x;
            node[pos].y = node[LSon(pos)].y + node[RSon(pos)].y;
        }
        void Build(int l, int r, int pos) {
            node[pos].x = node[pos].y = 0;
            node[pos].flag = 0;
            node[pos].degree = 0;
            if (l == r) {
                scanf("%lf", &node[pos].y);
                return;
            }
            int m = l + r >> 1;
            Build(l, m, LSon(pos));
            Build(m + 1, r, RSon(pos));
            Update(pos);
        }
        void Push(int pos) {
            Seg& father = node[pos];
            Seg& lson = node[LSon(pos)];
            Seg& rson = node[RSon(pos)];
            if (father.flag) {
                Rotate(lson, father.flag);
                Rotate(rson, father.flag);
                lson.flag += father.flag;
                rson.flag += father.flag;
                father.flag = 0;
            }
        }
        void Modify(int l, int r, int pos, int x, int y, int z) {
            if (x <= l && r <= y) {
                Rotate(node[pos], z);
                node[pos].flag += z;
                return;
            }
            Push(pos);
            int m = l + r >> 1;
            if (x <= m) Modify(l, m, LSon(pos), x, y, z);
            if (y > m) Modify(m + 1, r, RSon(pos), x, y, z);
            Update(pos);
        }
        int Query(int l, int r, int pos, int x) {
            if (l == r) return node[pos].degree;
            Push(pos);
            int m = l + r >> 1;
            if (x <= m) return Query(l, m, LSon(pos), x);
            else return Query(m + 1, r, RSon(pos), x);
        }
    };
    
    int n, c;
    int s, a;
    
    SegTree tree;
    
    double GetRad(int x);
    
    int main() {
        bool first = true;
        while (scanf("%d%d", &n, &c) != EOF) {
            tree.Build(0, n - 1, ROOT);   
            printf("%s", first ? first = false, "" : "
    ");
            for (int i = 0; i < c; i++) {
                scanf("%d%d", &s, &a);   
    //由于题目是逆时针转的,我的计算是顺时针,要加上180度,将后面的棒看成依然在Y轴,所以要减去后一个的角度 
                int degree = tree.Query(0, n - 1, ROOT, s - 1) + 180 + a -
                    tree.Query(0, n - 1, ROOT, s);
                tree.Modify(0, n - 1, ROOT, s, n - 1, degree);
                printf("%.2lf %.2lf
    ", tree.node[ROOT].x + EPS, tree.node[ROOT].y + EPS);
            }
        }
    
        return 0;
    }
    
    double GetRad(int x) {
        return x * PI / 180;
    }
    
    void Rotate(Seg& node, int degree) {
        double rad = GetRad(degree);
        double x = node.x; double y = node.y;
        node.x = x * cos(rad) + y * -sin(rad);
        node.y = x * sin(rad) + y * cos(rad);
        node.degree = (node.degree + degree) % 360;
    }
    
    
  • 相关阅读:
    智器SmartQ T7实体店试用体验
    BI笔记之SSAS库Process的几种方案
    PowerTip of the Day from powershell.com上周汇总(八)
    PowerTip of the Day2010071420100716 summary
    PowerTip of the Day from powershell.com上周汇总(十)
    PowerTip of the Day from powershell.com上周汇总(六)
    重新整理Cellset转Datatable
    自动加密web.config配置节批处理
    与DotNet数据对象结合的自定义数据对象设计 (二) 数据集合与DataTable
    在VS2003中以ClassLibrary工程的方式管理Web工程.
  • 原文地址:https://www.cnblogs.com/demian/p/6164613.html
Copyright © 2011-2022 走看看