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 初学线段可以做的水题,也是线段树的基础运用.也是我的第一个线段树的题. 题意:在区间范围内的最大值减去最小值 思路:线段树记录下每个区间内 ...
随机推荐
- shell基础练习题讲解
1037774765 克隆 1.创建一个用户redhat,其ID号为1001,基本组为like(组ID为2002),附近租为linux. groupadd -g 2002 likegroupadd l ...
- Android------BottonTabBar
前言:一款简单好用封装好的AndroidUI控件,底部导航栏. 1.使用 1.1添加 compile 'com.hjm:BottomTabBar:1.1.1' 1.2 activity_main. ...
- [OS] 多线程--第一次亲密接触CreateThread与_beginthreadex本质区别
转自:http://blog.csdn.net/morewindows/article/details/7421759 本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_be ...
- matlab exist函数
函数作用:用于确定某变量或值是否存在. 调用格式: exist主要有两种形式,一个参数和两个参数的,作用都是用于确定某值是否存在:1. b = exist( a) 若 a 存在,则 b = 1: 否则 ...
- 【bzoj1579】[Usaco2009 Feb]Revamping Trails 道路升级 分层图最短路
题目描述 每天,农夫John需要经过一些道路去检查牛棚N里面的牛. 农场上有M(1<=M<=50,000)条双向泥土道路,编号为1..M. 道路i连接牛棚P1_i和P2_i (1 < ...
- CF484E Sign on Fence && [国家集训队]middle
CF484E Sign on Fence #include<bits/stdc++.h> #define RG register #define IL inline #define _ 1 ...
- BZOJ4200 & 洛谷2304 & UOJ132:[NOI2015]小园丁与老司机——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4200 https://www.luogu.org/problemnew/show/P2304 ht ...
- LOJ6342::跳一跳——题解
https://loj.ac/problem/6342 f[i]表示从i开始跳的期望时间,f[n]=0. 所以f[i]=(f[i]+f[i+1]+……+f[n])/(n-i+1)+1. 移项整理可求f ...
- G. Trace ACM-ICPC 2018 徐州赛区网络预赛 线段树写法
There's a beach in the first quadrant. And from time to time, there are sea waves. A wave ( xx , yy ...
- Coconuts HDU - 5925 二维离散化 自闭了
TanBig, a friend of Mr. Frog, likes eating very much, so he always has dreams about eating. One day, ...