POJ 2991 - Crane

ICPC-report 专栏收录该内容
106 篇文章 0 订阅

Advanced Data Structures :: Segment Tree

 

Description

有一个机械臂,想象它以直角坐标系的原点为轴,一节连着一节,最末端为机械手。

你可以旋转任意一个关节,改变机械手的位置。

(有点像《cut the rope》中的机械臂?没错,就是那么个玩意)

而现在要做的就正是这么一件事。

告诉你机械臂每节手臂的长度,不停得去操作它(旋转某个关节)。

请求出每次操作之后机械手的位置。


Type

Advanced Data Structures :: Segment Tree


Analysis

刚开始看可能会质疑,很奇怪,这题和线段树有很什么关系。

其实这题是计算几何和线段树的合体。

我们把手臂都各自看成一个向量。
则机械手的位置正好是手臂向量之和。

旋转某个关节,其实就是把关节到机械手之间的手臂向量统统旋转。
由于手臂很多,要每个向量做相同的旋转操作很费时间。
这时候我们就可以想到用线段树的优势正是可以快速地成段更新。

其实和一般的成段更新题目没什么差别,只是我们通常只是成段替换、或者成段增加。
这时候要做的是,对向量成段得做旋转变换。
只要利用旋转变化矩阵即可。

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

Solution
#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, "" : "\n");
        for (int i = 0; i < c; i++) {
            scanf("%d%d", &s, &a);   
            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\n", 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;
}




  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值