POJ 2991 Crane(线段树)

【题目链接】 http://poj.org/problem?id=2991

【题目大意】

  给出一条竖直线,由很多线段组成,
  每次可以把中间某个节点的位置逆时针转动一定角度,求每次转动之后这条线终点位置。

【题解】

  用线段树维护线段的角度和向量有:
    vx[x]=vx[lson]+cosα*vx[rson]-sinα*vy[rson]
    vy[x]=vy[lson]+sinα*vx[rson]+cosα*vy[rson]
  对被影响的区段,更新其角度。

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const double PI=acos(-1.0);
const int ST_SIZE=(1<<15)-1;
const int MAX_C=10010,MAX_N=10010;
int Fi,N,C,L[MAX_N],S[MAX_C],A[MAX_C];
double vx[ST_SIZE],vy[ST_SIZE],ang[ST_SIZE],prv[MAX_N];
void build(int x,int l,int r){
    ang[x]=vx[x]=0.0;
    if(r-l==1){vy[x]=L[l];return;}
    build(x<<1|1,l,(l+r)>>1);
    build(x+1<<1,(l+r)>>1,r);
    vy[x]=vy[x<<1|1]+vy[x+1<<1];
}
void change(int s,double a,int x,int l,int r){
    if(s<=l)return;
    else if(s<r){
        change(s,a,x<<1|1,l,l+r>>1);
        change(s,a,(x+1)<<1,l+r>>1,r);
        if(s<<1<=l+r)ang[x]+=a;
        double s=sin(ang[x]),c=cos(ang[x]);
        vx[x]=vx[x<<1|1]+c*vx[x+1<<1]-s*vy[x+1<<1];
        vy[x]=vy[x<<1|1]+s*vx[x+1<<1]+c*vy[x+1<<1];
    }
}
int main(){
    while(~scanf("%d%d",&N,&C)){
    	  if(Fi++)puts("");
        for(int i=0;i<N;i++)scanf("%d",&L[i]);
        for(int i=0;i<C;i++)scanf("%d%d",&S[i],&A[i]);
        build(0,0,N);
        for(int i=1;i<N;i++)prv[i]=PI;
        for(int i=0;i<C;i++){
            int s=S[i];
            double a=A[i]/360.0*2*PI;
            change(s,a-prv[s],0,0,N);
            prv[s]=a;
            printf("%.2f %.2f
",vx[0],vy[0]);
        }
    }return 0;
}
原文地址:https://www.cnblogs.com/forever97/p/poj2991.html