原题链接: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;
}