POJ 2991 Crane(线段树+计算几何—更新向量)

Crane
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 4895   Accepted: 1313   Special Judge

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条线段首位相连,从1到n依次从下到上比值连接,指向上方,第i条线段的长度为Li。  有C条指令。指令i给出两个整数Si和Ai,效果是使得线段Si和Si+1之间的角度变成Ai度。其中角度指的是Si开始沿逆时针方向旋转到Si+1所经过的角度。最开始时左右角度都是180度。   在执行每条指令后,输出此时第N条线段的端点的坐标。设第一条线段的下端点坐标为 (0,0)。


题解:先我们了解一下两个基础的计算几何知识。


①:一个向量(x,y)沿逆时针方向旋转rad度后得到的向量为:

nx = x*cos(rad) - y*sin(rad)           ny = x*sin(rad) - y*cos(rad)

注意:题中向量旋转的角度应该是 rad - degree[i](degree[i]表示Si与Si+1在此时的夹角)。

②:如果一个折线是由多条向量连接而成的,那么这条向量的终点坐标一定等于 起点+向量1+向量2+...+向量n 。


知道了这两个知识点,再回头看这道题目。我们建立一棵线段树,来维护两个信息:1. 节点区间[l,r]所表示的向量的x[i] 和 y[i]。  2. 每个节点区间[l,r]所表示的区间需要逆时针旋转的角度(在计算时候我们需要转化为弧度)。在每次转动时我们需要更新 ql到n这些线段的向量。我们在update函数中查找到大于等于ql的区间左端点,更新区间,更新完毕后,记录下旋转角度,并更新它的自区间,然后将记录下的角度值变为0。


具体见代码:


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define maxn 10010
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define PI acos(-1.0)
int d[maxn*4],degree[maxn];
double x[maxn*4],y[maxn*4];

void cal(int i,int de)//计算旋转后的向量 
{
	double rad=PI*de/180.0;
	double nx=x[i]*cos(rad)-y[i]*sin(rad);
	double ny=x[i]*sin(rad)+y[i]*cos(rad);
	x[i]=nx;
	y[i]=ny;
}

void PushUp(int i)//更新父区间 
{
	x[i]=x[i*2]+x[i*2+1];
	y[i]=y[i*2]+y[i*2+1];
}

void PushDown(int i)//更新子区间 
{
	if(d[i])
	{
		d[i*2]+=d[i];//记录转动的角度 
		d[i*2+1]+=d[i];
		cal(i*2,d[i]);
		cal(i*2+1,d[i]);
		d[i]=0;//更新完子区间后,父区间转动角度还原 
	}
}

void build(int i,int l,int r)
{
	d[i]=0;
	if(l==r)
	{
		x[i]=0;
		scanf("%lf",&y[i]);
		return ;
	}
	int m=(l+r)/2;
	build(lson);
	build(rson);
	PushUp(i);
}

void update(int ql,int rad,int i,int l,int r)
{
	if(ql<=l)
	{
		cal(i,rad);
		d[i]+=rad;//记录下当前区间转动的角度,然后也将子区间所表示的线段转动同样的角度 
		return ;
	}
	PushDown(i);
	int m=(l+r)/2;
	if(ql<=m)
		update(ql,rad,lson);
	update(ql,rad,rson);
	PushUp(i);
}

int main()
{
	int n,m,i,sign=0;
	int ql,rad;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		if(sign)
			printf("\n");
		sign=1;
		build(1,1,n);
		for(i=1;i<n;++i)
			degree[i]=180;//每两个相邻线段之间的初始角度都是180度 
		while(m--)
		{
			scanf("%d%d",&ql,&rad);
			update(ql+1,rad-degree[ql],1,1,n);
			degree[ql]=rad;
			printf("%.2lf %.2lf\n",fabs(x[1])<1e-8 ? 0:x[1],fabs(y[1])<1e-8 ? 0:y[1]);
		}
	}
	return 0;
}  




你可能感兴趣的:(POJ 2991 Crane(线段树+计算几何—更新向量))