地址 http://poj.org/problem?id=2991

题解

本来以为这是一个简单的线段树模板 不料始终不太明白线段树如何记录转动角度后的各个线段端的XY值

学习了网络上的一些博客题解 感觉似是而非 谈到复数 角度 向量等,有点不太好理解

现在这里将自己的理解记录如下

如图

1 预备知识

使用线段树记录的内容如下  指示某段线段的组合 以第一条线段为垂直 最后的线段的端点的X Y值

途中1~2 线段 和3~5线段 就是线段树节点1~5的子节点 那么线段树节点1~5 就记录1~5结合后的X Y 值以及两个子节点结合的角度值

由于3~5线段的XY 是以自己的第一条线段为垂直起点为0 0 计算出来的X Y

那么在于1~2线段合并的时候 并不是简单的将两子节点的X Y相加即可得到1~5线段的XY 而是要加入旋转了相对角度 该角度由记录1~5线段的线段树节点记录

1~2线段部分的X Y值 旋转相对角度的公式推导如下

https://blog.csdn.net/hjq376247328/article/details/45113563

其实也就是

xNew = x * cosB - y * sinB

yNew = x * sinB + y * cosB

再来和 1~2线段的X Y相加即可得到1~5线段的X Y,并将该两子节点的相对角度记录在父节点中

预备知识讲完

2 解答步骤如下

一  建造线段树 build(1,1,n); 由于每条线段都是垂直连接 所以X 均为0 相对角度全部为0

void build(int k,int l,int r) {
angT[k]=x[k]=0.0;
if(r==l) y[k]=L[l];
else {
int lson=k*, rson=k*+;
int m=(l+r)/;
build(lson,l,m);
build(rson,m+,r);
y[k]=y[lson]+y[rson];
}
}

二 某段线段转动角度后

题意输入的角度是si和si+1逆时针角度而不是旋转的角度,而是需要转到的结果角度, 所以我们需要进行转换 。所以使用了pre数组记录每段线段与相邻线段的逆时针间隔角度,这样接收到题意输入角度a后

a-pre[s] 就是实际要转动的角度 而且需要更新pre[s] 以便下次计算

change(s,ang-pre[s],,,n);
pre[s]=ang; // 要求改变为a度 考虑之前已改变过

chang()代码就是批量更新需要转换的角度和X Y

只有旋转的起点线段在当前线段树节点的左子节点 我们才更新当前线段树节点的角度记录

如图

假设节点4 向3旋转90度

那么合并1 2的时候无更新

合并3 4的时候 3~4节点的角度要在原来记录上再旋转90

合并1~2 3~4为1~4的时候无需更新角度 因为 3~4 已经旋转 与1 ~2的相对角度 并没有变化

同样 5~8节点流程中无变化

但是合并1~4 5~8节点的时候 角度需要旋转90

整个流程下来  4 5~8 角度均旋转更新1次  符合题目的集合意义

最后返回1~8节点记录的X Y即可

代码如下

 #include <iostream>
#include <vector>
#include <math.h>
#include <stdio.h>
using namespace std;
const double PI = acos(-1.0);
const int N = ; int n, c, L[N];
double pre[N]; double angT[N << ];
double x[N << ], y[N << ]; void build(int k, int l, int r) {
angT[k] = x[k] = 0.0;
if (r == l) y[k] = L[l];
else {
int lson = k * , rson = k * + ;
int m = (l + r) / ;
build(lson, l, m);
build(rson, m + , r);
y[k] = y[lson] + y[rson];
}
} void change(int s, double ang, int k, int l, int r) {
if (s < l || l == r) return; // 操作位置不在范围内 或 区间长度为1 不作处理
else if (s <= r) {
int lson = k * , rson = k * + ;
int m = (l + r) / ;
change(s, ang, lson, l, m);
change(s, ang, rson, m + , r); // 先处理左右子区间
if (s <= m) angT[k] += ang; // 操作位置位于区间的左子区间内 可根据左右子区间的向量更新 double sina = sin(angT[k]), cosa = cos(angT[k]);
x[k] = x[lson] + (x[rson] * cosa - y[rson] * sina);
y[k] = y[lson] + (x[rson] * sina + y[rson] * cosa);
}
} /*
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
*/ int main()
{
while (~scanf("%d%d", &n, &c)) {
for (int i = ; i <= n; i++) {
scanf("%d", &L[i]);
pre[i] = PI;
}
build(, , n);
while (c--) {
int s, a; scanf("%d%d", &s, &a);
double ang = (double)a / 180.0*PI;
change(s, ang - pre[s], , , n);
pre[s] = ang;
printf("%.2f %.2f\n", x[], y[]);
}
printf("\n");
} return ;
}

poj 2991 起重机的更多相关文章

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

    POJ 2991 Crane 题目链接 题意:给定一个垂直的挖掘机臂.有n段,如今每次操作能够旋转一个位置,把[s, s + 1]专程a度,每次旋转后要输出第n个位置的坐标 思路:线段树.把每一段当成 ...

  2. AC日记——Crane poj 2991

    POJ - 2991 思路: 向量旋转: 代码: #include <cmath> #include <cstdio> #include <cstring> #in ...

  3. [poj 2991]Crane[线段树表示向量之和,而非数量]

    题意: 起重机的机械臂, 由n段组成, 对某一些连接点进行旋转, 询问每次操作后的末端坐标. 思路: 由于旋转会影响到该点之后所有线段的角度, 因此容易想到用线段树记录角度, 成段更新. (但是不是每 ...

  4. POJ 2991–Crane【线段树+几何】

    题意: 把手臂都各自看成一个向量,则机械手的位置正好是手臂向量之和.旋转某个关节,其实就是把关节到机械手之间的手臂向量统统旋转. 由于手臂很多,要每个向量做相同的旋转操作很费时间.这时就可以想到用线段 ...

  5. 线段树 poj 2991

    我们只要把这些向量求和,最终所指的位置就是终点,因此我们只要维护好向量的区间和就可以了.对于第二个问题,我们可以用一个数组degree[i]表示第i个向量和第i-1一个向量当前的夹角,这样就有了当前的 ...

  6. POJ 2991 Crane

    线段树+计算几何,区间更新,区间求和,向量旋转. /* *********************************************** Author :Zhou Zhentao Ema ...

  7. (中等) POJ 2991 Crane , 几何+线段树。

    Description ACM has bought a new crane (crane -- jeřáb) . The crane consists of n segments of variou ...

  8. Crane (POJ 2991)

    //线段树 延迟标签 // #include <bits/stdc++.h> using namespace std; const int maxn=1e4+5; double x[max ...

  9. POJ 2991 Crane(线段树)

    Crane Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7687   Accepted: 2075   Special J ...

随机推荐

  1. networkx整理

    1.基础知识 1.1.介绍 networkx在2002年5月产生,是一个用Python语言开发的图论与复杂网络建模工具,内置了常用的图与复杂网络分析算法,可以方便的进行复杂网络数据分析.仿真建模等工作 ...

  2. 2018.8.10 python中的迭代器

    主要内容: 1.函数名的使用 2.闭包 3.迭代器 一.函数名的运用 函数名是一个变量,但他是一个特殊的变量,与括号配合可执行函数的变量. 1.函数名的内存地址 def func(): print(' ...

  3. tomcat 7.0 免安装版配置

    转载请注明出处,欢迎邮件交流:1057449102@qq.com 注意:tomcat 运行是依赖jdk的,前提是要安装了jdk,配置好. 1.把下载好的 apache-tomcat-7.0.85.zi ...

  4. 初识mysql索引 - 小白篇

    :接触mysq也有两年左右的时间了,但是对该数据库的理解自认还比较初级,看过很多文章,也看过一些相关的书籍,依然小白....(这里个人总结是两点主要原因:1.对mysql的学习大部分都是源于看一些杂七 ...

  5. 第三十五章 POSIX共享内存

    POSIX共享内存函数介绍 shm_open 功能: 用来创建或打开一个共享内存对象 原型: int shm_open(const char *name, int oflag, mode_t mode ...

  6. [开源] gnet: 一个轻量级且高性能的 Golang 网络库

    Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...

  7. 建议收藏备用:.net core使用QRCoder生成普通二维码和带Logo的二维码详细使用教程,源码已更新至开源模板

    随着互联网越来越生活化,二维码的使用越来越普遍,不论是扫码支付还是扫码关注引流,似乎我们总是离不开二维码,那么很多需要推广的文章或社区想要自己的二维码,那么你是不是需要在网站直接提供给用户呢?很多开发 ...

  8. 近期学习es6后对变量提升及let和const的一点思考

    1.变量提升:(创建->初始化)-->赋值-->修改 就是说,以var声明的变量,它的声明会被提升到当前作用域的顶端(注意是变量声明提升,变量的赋值没有提升) //在if语句中也会提 ...

  9. P3106 [USACO14OPEN]GPS的决斗(最短路)

    化简:够简的了.....但是!翻译绝对有锅. 这个最短路是从n到每个点的单源最短路,也就是最短路径树. 那么,思路就很明确了.建两个图,然后跑两边SPFA,记录下最短路径. 然后,对于两点之间的边,如 ...

  10. 20190630A(贪心)

    题目描述 约翰留下他的N只奶牛上山采木.他离开的时候,她们像往常一样悠闲地在草场里吃草.可是,当他回来的时候,他看到了一幕惨剧:牛们正躲在他的花园里,啃食着他心爱的美丽花朵!为了使接下来花朵的损失最小 ...