http://www.tsinsen.com/ViewGProblem.html?gpid=-1000001307
【题意】
有一些编号为1..n的点,每个都有其初始坐标,有六类操作
Move i j a b 编号i到编号j的点移动一个(a,b)的向量
Patrol i j a 编号i到编号j的点绕原点逆时针旋转a(弧度制)
Lurk i j 编号i到编号j的点全部坐标变为(0,0)
Cancel a 撤销最后a次操作
Redo a 重做最后a次操作
Ask i 询问第i个点的坐标
数据不会进行不合法的操作,比如
Cancel 3
Move 1 1 1 1
Redo 2
【输入】
第一行一个n
接下来n行每行一个点的初始坐标
之后一行是个q,表示操作数
接下来每行一个操作
【输出】
对于Ask 操作,输出答案,误差小于0.0001即视为正确,有check。
这个询问只询问一个点……记录下对于每个点的操作就可以知道这个点的坐标了……
不过这个也询问不了一段区间……问区间问什么啊
对于Move操作,还是很好想到如何处理的吧
关键在于Patrol操作了
假设旋转角度为a,当前与x正半轴的夹角为b,距原点的距离为r=sqrt(x^2+y^2)
旋转前坐标为(r cos(b),r sin (b))
旋转后坐标为(r cos(a+b),r sin(a+b))
作为一个高中生,自然可以将其展开
旋转后坐标即为(r(cos(a)cos(b)-sin(a)sin(b)),r(sin(a)cos(b)+cos(a)sin(b)))=(cos(a)x-sin(a)y,sin(a)x+cos(a)y)
这么一来,就比较容易想到矩阵乘法了
假设不考虑Cancel和Redo的操作
用一个线段树来描述对于每个点的操作就好了,每个节点记录一个矩阵,询问一个节点的时候,只要把访问过的矩阵乘起来就好了
不过矩阵具有结合律,不具有交换律,所以维护矩阵的顺序性很重要,所以访问一个节点的话先下移懒标记,然后再进行插入操作
询问的话,询问过程中不要下传懒标记,把路过的节点的矩阵从底到顶乘起来就好了
接下来解决如何支持Cancel和Redo操作
仔细想想的话,Cancel就是回到过去线段树的某个状态,Redo就是再回去
这样我们把线段树所有状态记录下来不就好了吗
这个不可能吗……这个当然可能,一切的一切都开始于范浩强冬令营讲稿提到的一个概念 《函数性编程》
进行一个操作,新的线段树和原来的线段树有什么区别呢,区别在于有几个节点信息不一样
具体是几个节点呢,就是访问过的节点,最多logn个
那么我们可以把这些节点复制一份,连接上原来不需要修改的节点
这样每个节点可能有多个父亲节点,但子节点是唯一的……
即从上到下查询线段树得到的树是唯一的,即只要记录下每个状态的根节点,就可以查询到这个状态的树的信息……等价于记录了线段树的所有状态
这样就解决了这道题
函数性编程是十分前沿的一种思路,感觉高端选手都对此有很多的看法,需要注意吧
{$inline on} program crisis; uses math; const eps=1e-10; maxn=3000000; type matrix=array [1..3,1..3] of double; var now,tot,n,q,i,j,k:longint; a,b:double; root:array [0..50001] of longint; x,y:array [0..20001] of double; belong,left,right:array [0..maxn] of longint; mat:array [0..maxn] of matrix; squ:matrix; order:char; function matrixmul (a,b:matrix):matrix;inline; var ans:matrix; i,j,k:longint; begin fillchar(ans,sizeof(ans),0); for i:=1 to 3 do for k:=1 to 3 do if abs(a[i,k])>eps then for j:=1 to 3 do ans[i,j]:=ans[i,j]+a[i,k]*b[k,j]; exit(ans); end; procedure maketree (s,e,now:longint); var mid:longint; begin belong[now]:=0; for mid:=1 to 3 do mat[now][mid,mid]:=1; if s=e then exit; mid:=(s+e) div 2; inc(tot); left[now]:=tot; maketree(s,mid,tot); inc(tot); right[now]:=tot; maketree(mid+1,e,tot); end; procedure update (now:longint);inline; begin if left[now]=0 then exit; if belong[left[now]]<>belong[now] then begin inc(tot); mat[tot]:=mat[left[now]]; left[tot]:=left[left[now]]; right[tot]:=right[left[now]]; belong[tot]:=belong[now]; left[now]:=tot; end; mat[left[now]]:=matrixmul(mat[left[now]],mat[now]); if belong[right[now]]<>belong[now] then begin inc(tot); mat[tot]:=mat[right[now]]; left[tot]:=left[right[now]]; right[tot]:=right[right[now]]; belong[tot]:=now; right[now]:=tot; end; mat[right[now]]:=matrixmul(mat[right[now]],mat[now]); fillchar(mat[now],sizeof(mat[now]),0); mat[now][1,1]:=1; mat[now][2,2]:=1; mat[now][3,3]:=1; end; procedure insert (s,e,l,r,last,now:longint); var mid:longint; begin update(last); mat[now]:=mat[last]; if (s=l)and(e=r) then begin mat[now]:=matrixmul(mat[now],squ); left[now]:=left[last]; right[now]:=right[last]; exit; end; mid:=(l+r) div 2; if e<=mid then begin inc(tot); left[now]:=tot; belong[tot]:=now; mat[tot]:=mat[left[last]]; right[now]:=right[last]; insert(s,e,l,mid,left[last],left[now]); end else if s>mid then begin left[now]:=left[last]; inc(tot); right[now]:=tot; belong[tot]:=belong[now]; mat[tot]:=mat[right[last]]; insert(s,e,mid+1,r,right[last],right[now]) end else begin inc(tot); left[now]:=tot; belong[tot]:=now; mat[tot]:=mat[left[last]]; inc(tot); right[now]:=tot; belong[tot]:=belong[now]; mat[tot]:=mat[right[last]]; insert(s,mid,l,mid,left[last],left[now]); insert(mid+1,e,mid+1,r,right[last],right[now]); end; end; function find (x,l,r,now:longint):matrix; begin if l=r then exit(mat[now]); if x<=(l+r) div 2 then exit(matrixmul(find(x,l,(l+r) div 2,left[now]),mat[now])) else exit(matrixmul(find(x,(l+r) div 2 +1,r,right[now]),mat[now])); end; begin read(n); for i:=1 to n do read(x[i],y[i]); maketree(1,n,0); readln(q); while q>0 do begin dec(q); read(order); case order of 'M':begin read(order,order,order); read(i,j,a,b); fillchar(squ,sizeof(squ),0); squ[1,1]:=1; squ[2,2]:=1; squ[3,3]:=1; squ[3,1]:=a; squ[3,2]:=b; inc(tot); inc(now); root[now]:=tot; belong[tot]:=now; insert(i,j,1,n,root[now-1],root[now]); end; 'P':begin read(order,order,order,order,order); read(i,j,a); fillchar(squ,sizeof(squ),0); squ[1,1]:=cos(a); squ[2,1]:=-sin(a); squ[1,2]:=sin(a); squ[2,2]:=cos(a); squ[3,3]:=1; inc(tot); inc(now); root[now]:=tot; belong[tot]:=now; insert(i,j,1,n,root[now-1],root[now]); end; 'L':begin read(order,order,order); read(i,j); fillchar(squ,sizeof(squ),0); squ[3,3]:=1; inc(tot); inc(now); root[now]:=tot; belong[tot]:=now; insert(i,j,1,n,root[now-1],root[now]); end; 'C':begin read(order,order,order,order,order); read(i); now:=now-i; end; 'R':begin read(order,order,order); read(i); now:=now+i; end; 'A':begin read(order,order); read(i); squ:=find(i,1,n,root[now]); writeln(x[i]*squ[1,1]+y[i]*squ[2,1]+squ[3,1]:0:5,' ', x[i]*squ[1,2]+y[i]*squ[2,2]+squ[3,2]:0:5); end; end; readln; end; end.