poj2991 Crane (线段树)

原题链接:poj2991 Crane
题意:有N条线段,第i条线段长度为Li,开始首尾竖直相连,(1<=L<=100;1<=Si<N,0<=Ai<=359)

            有C条指令,每条指令i给出Si和Ai:把Si和Si+1之间的角度变成Ai,起始角度都是180度

            第一个点坐标为(0, 0),执行每条指令后,输出第N个点的坐标

输入

2 1
10 5
1 90

输出

5.00 10.00
#include <cstdio>
#include <cmath>
const int ST_SIZE = (1 << 15) - 1;
const int MAX_N = 10001;
const int MAX_C = 10001;
const double PI = 3.14159265;
int N, C;
int L[MAX_N];
int S[MAX_C], A[MAX_N];
//线段树维护的数据 
double vx[ST_SIZE], vy[ST_SIZE];//各节点向量,(vx[0],vy[0])代表根节点维护的(0, N)区间的向量(亦是要求的坐标) 
double ang[ST_SIZE];	//各节点的角度 
double prv[MAX_N];	//为了查询角度的变化而保存的当前角度的数组 

void init(int k, int l, int r){	//初始化线段树 
	ang[k] = vx[k] = 0.0;	//k是节点的编号,l,r表示当前结点对应的是[l, r)区间 
	if(r - l == 1)
		vy[k] = L[l];	//叶节点 
	else{	//非叶节点 
		int chl = k * 2 + 1, chr = k * 2 + 2;
		init(chl, l, (l + r) / 2);
		init(chr, (l + r) / 2, r);
		vy[k] = vy[chl] + vy[chr];//递归自下而上初始化 
	}
}
//把s和s+1的角度变为a,v是节点的编号,l,r表示当前结点对应的区间是[l, r)区间 
void change(int s, double a, int v, int l, int r) {	
	if(s <= l)	return ;	//s段不在区间中 
	else if(s < r){
		int chl = v * 2 + 1, chr = v * 2 + 2;
		int m = (l + r) / 2;
		change(s, a, chl, l, m);
		change(s, a, chr, m, r);
		if(s <= m)	ang[v] += a;	//节点旋转角度的变化
		double s = sin(ang[v]), c = cos(ang[v]);	//递归,s段向上改变向量值
		vx[v] = vx[chl] + (c * vx[chr] - s * vy[chr]);
		vy[v] = vy[chl] + (s * vx[chr] + c * vy[chr]);
	}
}
void solve(){
	init(0, 0, N);	//初始化 
	for(int i = 1;i < N;i ++)	prv[i] = PI; //当前结点的角度为PI 
	for(int i = 0;i < C;i ++){
		int s = S[i];
		double a = A[i] / 180.0 * PI; //角度转化为弧度0
		change(s, a - prv[s], 0, 0, N);	//节点s有s+1角度变为a需要旋转a-prv[s],从根节点开始 
		prv[s] = a;	//当前节点角度变为a
		printf("%.2f %.2f\n", vx[0], vy[0]);
	}
}
int main(){
	//printf("%.12lf\n", M_PI);
	while(~scanf("%d%d", &N, &C)){
		int i;
		for(i = 0;i < N;i ++)
			scanf("%d", &L[i]);
		for(i = 0;i < C;i ++)
			scanf("%d%d", &S[i], &A[i]);
		solve();
	}
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值