1. 程式人生 > >Crane /// 向量旋轉+線段樹

Crane /// 向量旋轉+線段樹

題目大意:

給定n條首尾相接的線段的長度

第一條從0,0開始,所有線段垂直與x軸向上延伸

給定c次操作 每次操作給定 s,a

使得 由第s條線段的角度 逆時針旋轉a後 達到第s+1條線段的角度

每次操作後輸出最後一條線段末尾端點的座標

 

向量逆時針旋轉公式為

x' = x * cos(A) - y * sin(A); y' = x * sin(A) + y * cos(A);

 

一個向量  (x,y)  可分解兩個向量為 垂直於y軸的(x,0) 和垂直於x軸的 (0,y)

兩個分向量逆時針A度後

(x',0) = ( x*coa(A),x*sin(A) )   (0,
y') = ( -y*sin(A),y*cos(A) )

兩個旋轉後的分向量 再合併就可得到旋轉後的 (x',y')

 

用線段樹維護一段區間內由 該區間內第一段線段的起點 指向 最後一段線段的末尾的向量

每次操作更新區間時 我們只對 操作位置處於當前區間的左子區間 的區間更新

那麼這樣當更新一段區間時 當前向量=左子區間的向量+右子區間旋轉後的的向量

並且對於區間長度為1的區間不做處理

#include <bits/stdc++.h>
using namespace std;
const double PI=acos(-1.0);
const int N=10005; int n,c,L[N]; double pre[N]; double angT[N<<2]; double x[N<<2],y[N<<2]; void build(int k,int l,int r) { angT[k]=x[k]=0.0; if(r==l) y[k]=L[l]; else { int lson=k*2, rson=k*2+1; int m=(l+r)/2; build(lson,l,m); build(rson,m+1,r); y[k]
=y[lson]+y[rson]; } } void change(int s,double ang,int k,int l,int r) { if(s<l || l==r) return; // 操作位置不在範圍內 或 區間長度為1 不作處理 else if(s<=r) { int lson=k*2, rson=k*2+1; int m=(l+r)/2; change(s,ang,lson,l,m); change(s,ang,rson,m+1,r); // 先處理左右子區間 if(s<=m) angT[k]+=ang; // 操作位置位於區間的左子區間內 可根據左右子區間的向量更新 double sina=sin(angT[k]), cosa=cos(angT[k]); x[k]=x[lson]+(x[rson]*cosa-y[rson]*sina); y[k]=y[lson]+(x[rson]*sina+y[rson]*cosa); } } int main() { while(~scanf("%d%d",&n,&c)) { for(int i=1;i<=n;i++) { scanf("%d",&L[i]); pre[i]=PI; } build(1,1,n); while(c--) { int s,a; scanf("%d%d",&s,&a); double ang=(double)a/180.0*PI; change(s,ang-pre[s],1,1,n); pre[s]=ang; // 要求改變為a度 考慮之前已改變過 printf("%.2f %.2f\n",x[1],y[1]); } printf("\n"); } return 0; }
View Code