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-141-x的平方根
141-x的平方根 实现 int sqrt(int x) 函数,计算并返回 x 的平方根. 样例 sqrt(3) = 1 sqrt(4) = 2 sqrt(5) = 2 sqrt(10) = 3 挑战 ...
- TCP系列18—重传—8、FACK及SACK reneging下的重传
一.介绍 FACK的全称是forward acknowledgement,FACK通过记录SACK块中系列号最大(forward-most)的SACK块来推测丢包信息,在linux中使用fackets ...
- Debian 7 amd64--TP-LINK TL-WN725N 2.0源码驱动编译安装
租房用的是无线网络,在新安装的Debian 7 amd64使用的无线网卡型号是TP-LINK TL-WN725N 2.0,发现驱动安装还是有些问题,折腾了很久,特意在此记录一下. TL-WN725N ...
- 使用协程(gevent)实现请求
协程,又称微线程.英文名Coroutine. 协程最大的优势就是协程极高的执行效率.因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就 ...
- Qt自定义标题栏
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt自定义标题栏 本文地址:http://techieliang.com/2017/1 ...
- 转 linux安装swoole扩展
linux安装swoole扩展 发表于2年前(2014-09-03 14:05) 阅读(4404) | 评论(3) 7人收藏此文章, 我要收藏 赞2 上海源创会5月15日与你相约[玫瑰里],赶快来 ...
- 播放MP3
播放背景音乐 上文来自:http://blog.csdn.net/henulwj/article/details/8977738 using System; using System.Collecti ...
- BZOJ4737 组合数问题(卢卡斯定理+数位dp)
不妨不管j<=i的限制.由卢卡斯定理,C(i,j) mod k=0相当于k进制下存在某位上j大于i.容易想到数位dp,即设f[x][0/1][0/1][0/1]为到第x位时是否有某位上j> ...
- Andorid API Package ---> android
包名: android Added in API level 1 URL:http:// ...
- 消息传递 树形DP
非常妙的树形DP:由于n很小,我们可以枚举每一个点作为第一个节点,计算其时间花费 那么问题就转化为对于给点节点求花费时间. 通过观察,显然我们会发现先传给花费时间多的人更加合算,因为这样可以最大限度的 ...