原题
题目链接
题目分析
本题可以用线段树来做.线段树的每个区间维护的值如下.(1)该区间的第一段竖直放置时,从该区间头端指向尾端的向量.(2)拼接两个子区间(如果有子区间的话)时,右儿子需要旋转的角度.这里说明一下,当一个子区间被修改时,如果它是父区间的右儿子,则父区间维护的角度不用变,如果它是父区间的左儿子,则父区间维护的角度需要累加旋转角度.当然该子区间也需要累加旋转角度.也就是说当一个线段被旋转之后,编号比它大的线段都需要旋转相同的角度.拼接两个子区间的公式如下:
x0=x1+(x2*cos(ang)-y2*sin(ang));
y0=y1+(y2*cos(ang)+x2*sin(ang));
其中(x0,y0)是当前区间维护的向量,(x1,y1)是左儿子向量,(x2,y2)是右二子向量,ang是当前区间维护的角度.x1,y1后面的表达式由右儿子向量旋转得出.
还有注意线段树维护的是旋转角度(即改变量),输入的时候不能直接输入题目给的数据,得保留上一次的角度,做差之后才能得到旋转角度.
代码
1 #include2 #include 3 #include 4 5 using namespace std; 6 typedef unsigned long long ULL; 7 typedef long long LL; 8 typedef long double LB; 9 const int INF_INT=0x3f3f3f3f; 10 const LL INF_LL=0x3f3f3f3f3f3f3f3f; 11 12 const double pi=acos(-1.0); 13 14 typedef struct{int l,r;double x,y,ang;} Node; 15 Node tree[4*10000]; 16 double pre[20000]; 17 18 void build(int k,int l,int r) 19 { 20 tree[k].l=l,tree[k].r=r,tree[k].ang=0,tree[k].x=0; 21 if(l==r) 22 { 23 scanf("%lf",&tree[k].y); 24 return ; 25 } 26 int mid=(l+r)/2; 27 build(2*k+1,l,mid);build(2*k+2,mid+1,r); 28 tree[k].y=tree[2*k+1].y+tree[2*k+2].y; 29 } 30 31 void modify(int k,int s,double ang) //修改函数 32 { 33 int mid=(tree[k].l+tree[k].r)/2; //区间的拼接位置位于mid 34 35 if(s 2*k+1,s,ang),tree[k].ang+=ang; 36 else if(s>mid) modify(2*k+2,s,ang); 37 else tree[k].ang+=ang; 38 39 tree[k].x=tree[2*k+1].x+(tree[2*k+2].x*cos(tree[k].ang)-tree[2*k+2].y*sin(tree[k].ang)); 40 tree[k].y=tree[2*k+1].y+(tree[2*k+2].y*cos(tree[k].ang)+tree[2*k+2].x*sin(tree[k].ang)); 41 } 42 43 int main() 44 { 45 // freopen("testdata.in","r",stdin); 46 // freopen("std.out","w",stdout); 47 int N,C; 48 while(cin>>N&&cin>>C) 49 { 50 for(int i=1;i<=N;i++) pre[i]=pi; 51 build(0,1,N); 52 for(int i=0;i ) 53 { 54 int s,ang; 55 scanf("%d %d",&s,&ang); 56 double t=pi*ang/180; //输入处理,转化为弧度制并求出旋转角度 57 modify(0,s,t-pre[s]); 58 pre[s]=t; 59 printf("%.2f %.2f\n",tree[0].x,tree[0].y); 60 } 61 cout<<endl; 62 } 63 return 0; 64 }