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;
    }
    
    
  • 相关阅读:
    NOIP 2012 T5 借教室 [洛谷P1083]
    POJ2437 Muddy roads
    POJ2288 Islands and Bridges
    洛谷P2014 TYVJ1051 选课
    POJ1741 Tree
    CODEVS1995 || TYVJ1863 黑魔法师之门
    TYVJ1939 玉蟾宫
    TYVJ1305 最大子序和
    POJ1737 Connected Graph
    TYVJ1864 守卫者的挑战
  • 原文地址:https://www.cnblogs.com/demian/p/6164613.html
Copyright © 2011-2022 走看看