POJ 2991 Crane (线段树)
Description
ACM has bought a new crane (crane -- jeřáb) . The crane consists of n segments of various lengths, connected by flexible joints. The end of the i-th segment is joined to the beginning of the i + 1-th one, for 1 ≤ i < n. The beginning of the first segment is fixed at point with coordinates (0, 0) and its end at point with coordinates (0, w), where w is the length of the first segment. All of the segments lie always in one plane, and the joints allow arbitrary rotation in that plane. After series of unpleasant accidents, it was decided that software that controls the crane must contain a piece of code that constantly checks the position of the end of crane, and stops the crane if a collision should happen.
Your task is to write a part of this software that determines the position of the end of the n-th segment after each command. The state of the crane is determined by the angles between consecutive segments. Initially, all of the angles are straight, i.e., 180o. The operator issues commands that change the angle in exactly one joint.
Input
The input consists of several instances, separated by single empty lines.
The first line of each instance consists of two integers 1 ≤ n ≤10 000 and c 0 separated by a single space -- the number of segments of the crane and the number of commands. The second line consists of n integers l1,..., ln (1 li 100) separated by single spaces. The length of the i-th segment of the crane is li. The following c lines specify the commands of the operator. Each line describing the command consists of two integers s and a (1 ≤ s < n, 0 ≤ a ≤ 359) separated by a single space -- the order to change the angle between the s-th and the s + 1-th segment to a degrees (the angle is measured counterclockwise from the s-th to the s + 1-th segment).
Output
The output for each instance consists of c lines. The i-th of the lines consists of two rational numbers x and y separated by a single space -- the coordinates of the end of the n-th segment after the i-th command, rounded to two digits after the decimal point.
The outputs for each two consecutive instances must be separated by a single empty line.
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
题意:
一开始有n根可任意折叠且首尾相连的木棍,木棍都在y轴上并有自己的长度,有m次操作,每次操作将i和i+1木棍的夹角调成给出的角度,每次调整输出木棍末端的坐标。
分析:
首先一个向量(x,y)逆时针绕起点旋转rad度后得到的向量为:
newx = xcos(rad)-ysin(rad) newy = xsin(rad)+ycos(rad)
然后我们要知道另外一个事实:如果一条折线由多个向量构成,那么这条折线段的终点坐标一定为起点+向量1+向量2+..+向量n的结果.如下图:
起点(0,0)经过了4个向量终点必定为(6,2).
在本题中我们构造一个线段树,该树维护2个信息:
第一个是本节点(假设控制区间[1,4] )所指代的那几段线段的终点-起点坐标构成的向量x[MAXN4]与y[MAXN4].在上面的图中反应出来就是1号节点维护向量(6,2).虽然1号节点维护的1-4段线段可能构成的是折线,但是我们不管,我们只考虑1号节点的终点和起点整体构成的向量.
第二个是本节点控制的那几个基本段所需要逆时针旋转的度数dMAXN*4.该信息对本节点没用,但是能通过PushDown操作来更新本节点的左右子节点的向量,使得左右子节点控制的向量逆时针旋转相应的角度.现在有个问题就是,如果整体(A+B两个向量相加后构成的一条直线向量)向量需要逆时针旋转rad度的话且最终旋转后的新向量为VC.此时如果我分别旋转A向量rad度和B向量rad度,然后用旋转后的向量A’和B’相加得到VC’,那么VC是否等于VC’呢?
这个问题等价于我PushDown
父节点维护的逆时针旋转度数后,我对左右子节点执行完旋转后,左右子节点表示的向量是否和父节点表示的向量能统一起来.答案是肯定的. 想象下面的图如果整体向量逆转RAD度,折线A+B也逆转RAD度,它们依然构成这个三角形.(此时B是随着A逆转的,不过就算B单独逆转,它形成的新向量与A’相加结果依然相同.因为A与B的相对角度始终不变,这个需要自己画图验证一下).
首先每个节点的x和y信息都是最新的,d度数只不过是在有需要的时候下放给其儿子,更新儿子的.
build(i,l,r)操作: 如果l==r,则读入长度y[i],令x[i]=0,d[i]=0.返回.否则分别建立左右子树,最后执行PushUp操作.
PushUp(i)操作: 用儿子们的向量相加赋值给父亲的x和y即可.
PushDown(i)操作: 如果d[i]!=0,那么就旋转左右儿子d[i]度,并令
d[i2] +=d[i]; d[i2+1] += d[i];
update(ql,rad,i,l,r)操作: 把第ql块到第n块的段逆时针旋转rad度.如果ql<=l,那么直接旋转当前节点i,并更新d[i]=rad即可.否则先PushDown操作,然后分段判断update,如果m>=ql,那么需要update左边那段,否则只需要update右边这段.最终还要PushUp.
rotate(i,deg)操作:将角度转化为弧度,得到旋转后的新坐标,然后更新i节点的x和y坐标.
query操作不需要每次只需要查看1节点的向量就是终点坐标.
这里有一点需要注意的,题目中每条指令输入的是i段逆时针旋转d度后到i+1段的位置,但是我们update操作用的是绝对的逆时针旋转度数rad.所以我们需要一个degree[n]数组,其中degree[i]=x表示当前(本次update之前)第i段需要逆转x度才能到i+1段.所以第i+1段到n段实际需要逆转的度数是:d-degree[i]. 自己画图想一想.
代码:
#include<iostream>
#include<stdio.h>
#include<cmath>
#define lchild left,mid,root<<1
#define rchild mid+1,right,root<<1|1
using namespace std;
const int mm=11111;
int sd[mm<<2],degree[mm<<2];
double sx[mm<<2],sy[mm<<2];
void push_up(int root)///通过左右子树,将当前结点的横纵坐标更新
{
sx[root]=sx[root<<1]+sx[root<<1|1];
sy[root]=sy[root<<1]+sy[root<<1|1];
}
///构建线段树
void build(int left,int right,int root)
{
sd[root]=0;///刚开始的时候,所有的线段都是竖直的
if(left==right)///找到了叶子节点
{
scanf("%lf",&sy[root]);
sx[root]=0;
return ;
}
int mid = (left + right)>>1;
build(lchild);///递归构建左子树
build(rchild);///递归构建右子树
push_up(root);///更新根节点的横纵坐标
}
void rotate(int root,int sd)
{
double d=sd*asin(1.0)/90.0;///将输入的角度转换为弧度,然后将这个点的横纵坐标更新
double x=cos(d)*sx[root]-sin(d)*sy[root];
double y=sin(d)*sx[root]+cos(d)*sy[root];
sx[root]=x;
sy[root]=y;
}
void push_down(int root)
{
rotate(root<<1,sd[root]);
rotate(root<<1|1,sd[root]);
///标记落在下一层
sd[root<<1]+=sd[root];
sd[root<<1|1]+=sd[root];
///释放本层标记
sd[root]=0;
}
void update(int p,int d,int left,int right,int root)
{
///右孩子整棵树的所有的节点肯定是要更新的
if(p<left)
{
rotate(root,d);
sd[root]+=d;
return ;
}
///更新角度
if(sd[root]) push_down(root);
int mid=(left+right)>>1;
if(p<mid) update(p,d,lchild);///如果涉及左孩子的区间,就将左孩子的区间更新
update(p,d,rchild);///右孩子的区间无论如何肯定是要进行更新的
push_up(root);
}
int main()
{
int i,j,n,m,flag=0;
while(~scanf("%d%d",&n,&m))
{
if(flag) puts("");///标记第几组数据,控制中间的空行
else
flag=1;
build(1,n,1);///首先建树,分别代表最区间,右区间,以及根节点
for(i=1;i<n;i++)
degree[i]=180;///表示最开始的时候的角度
while(m--)
{
scanf("%d%d",&i,&j);///将第i根木棍和第i+1根木棍之间的夹角调整成j度
update(i,j-degree[i],1,n,1);
degree[i]=j;
printf("%.2lf %.2lf\n",fabs(sx[1])<1e-8?0:sx[1],fabs(sy[1])<1e-8?0:sy[1]);
}
}
return 0;
}
POJ 2991 Crane (线段树)的更多相关文章
- [poj 2991]Crane[线段树表示向量之和,而非数量]
题意: 起重机的机械臂, 由n段组成, 对某一些连接点进行旋转, 询问每次操作后的末端坐标. 思路: 由于旋转会影响到该点之后所有线段的角度, 因此容易想到用线段树记录角度, 成段更新. (但是不是每 ...
- POJ 2991 Crane(线段树+计算几何)
POJ 2991 Crane 题目链接 题意:给定一个垂直的挖掘机臂.有n段,如今每次操作能够旋转一个位置,把[s, s + 1]专程a度,每次旋转后要输出第n个位置的坐标 思路:线段树.把每一段当成 ...
- POJ.2299 Ultra-QuickSort (线段树 单点更新 区间求和 逆序对 离散化)
POJ.2299 Ultra-QuickSort (线段树 单点更新 区间求和 逆序对 离散化) 题意分析 前置技能 线段树求逆序对 离散化 线段树求逆序对已经说过了,具体方法请看这里 离散化 有些数 ...
- Buy Tickets POJ - 2828 思维+线段树
Buy Tickets POJ - 2828 思维+线段树 题意 是说有n个人买票,但是呢这n个人都会去插队,问最后的队列是什么情况.插队的输入是两个数,第一个是前面有多少人,第二个是这个人的编号,最 ...
- (中等) POJ 2991 Crane , 几何+线段树。
Description ACM has bought a new crane (crane -- jeřáb) . The crane consists of n segments of variou ...
- POJ 2991 Crane(线段树)
Crane Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 7687 Accepted: 2075 Special J ...
- POJ 2991–Crane【线段树+几何】
题意: 把手臂都各自看成一个向量,则机械手的位置正好是手臂向量之和.旋转某个关节,其实就是把关节到机械手之间的手臂向量统统旋转. 由于手臂很多,要每个向量做相同的旋转操作很费时间.这时就可以想到用线段 ...
- POJ - 2991 Crane (段树+计算几何)
Description ACM has bought a new crane (crane -- jeřáb) . The crane consists of n segments of variou ...
- poj 3264(线段树)
http://poj.org/problem?id=3264 初学线段可以做的水题,也是线段树的基础运用.也是我的第一个线段树的题. 题意:在区间范围内的最大值减去最小值 思路:线段树记录下每个区间内 ...
随机推荐
- lintcode-14-二分查找
二分查找 给定一个排序的整数数组(升序)和一个要查找的整数target,用O(logn)的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1. 样例 在数组 ...
- python实现post请求
今天无论如何都要留下一些什么东西... 可以说今天学到一个新的一个东西,也需要分享出来,给更多的人去使用. 今天爬取的数据里面是客户端向服务器端发送加密过的token和一些页码之类的一个数据.(我主要 ...
- Chrome Extensions API & options
Chrome Extensions API options https://developer.chrome.com/extensions https://developer.chrome.com/e ...
- 进程间通讯-2(pipe)
通过pipe 管道的方式也可以实现进程间通信. 父进程和子进程之间可以实现相互通信. from multiprocessing import Process, Pipe def f(conn): co ...
- JSON字符串和Javascript对象字面量
JSON字符串和Javascript对象字面量 JSON是基于Javascript语法的一个子集而创建的,特别是对象和数组字面量语法. 正是由于JSON的这种特殊来历,导致很多Javascript程序 ...
- 未找到与约束ContractName Microsoft.VisualStudio.Text.ITextDocumentFactoryService...匹配的导出
安装微软的windows补丁 KB2781514 ,补丁主要解决“在 .NET Framework 4.5 更新之后,Visual Studio 用户可能无法打开或创建 C++ 或 JavaScrip ...
- PowerDesigner 建表语句没出现字段描述
填写了Name 还是没有 修改步骤如下:
- 【刷题】BZOJ 3527 [Zjoi2014]力
Description 给出n个数qi,给出Fj的定义如下: 令Ei=Fi/qi,求Ei. Input 第一行一个整数n. 接下来n行每行输入一个数,第i行表示qi. n≤100000,0<qi ...
- bzoj 1862: [Zjoi2006]GameZ游戏排名系统 & bzoj 1056: [HAOI2008]排名系统
傻叉了一晚上,把t打成x,然后这题神奇在于输出一段数,不足的不用输出,一开始我的是直接找没有后面就退,然后这样会格式错误囧……然后最后zj的还卡了下空间,于是不用string就过了……string毁一 ...
- HDU1561:The more, The Better——题解
http://acm.hdu.edu.cn/showproblem.php?pid=1561 ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允 ...