AtCoder ABC 189 E - Rotate and Flip
E - Rotate and Flip 补题
题意
平面上有n个点,有m次操作,每次操作涉及点的绕原点旋转与关于直线对称。之后q次询问,每次询问某个点在某次操作后的坐标
题解
果然大一线代没学好后患无穷。每次操作可以视为点的坐标乘一个变换矩阵,左乘右乘都可以,这样第\(i\)次操作之后点的位置就是初始坐标乘上前\(i\)个变换矩阵的累乘。前两个操作的变换矩阵比较好构造,后两个例如\((x,y)=>(2p-x, y)\),变换之后横坐标多了个常数,构造出的变换矩阵含\(x\),但我们希望每个变换矩阵都与点的坐标无关,所以可以给点的坐标多加一维,变成\((x, y, 1)\),这样可以构造出四种操作的变换矩阵:
\[ \begin{bmatrix}
0 & -1 & 0 \\
1 & 0 & 0 \\
0 & 0 & 1
\end{bmatrix} \tag{1}
\]
\[ \begin{bmatrix}
0 & -1 & 0 \\
1 & 0 & 0 \\
0 & 0 & 1
\end{bmatrix} \tag{2}
\]
\[ \begin{bmatrix}
-1 & 0 & 0 \\
0 & 1 & 0 \\
2p & 0 & 1
\end{bmatrix} \tag{3}
\]
\[ \begin{bmatrix}
1 & 0 & 0 \\
0 & -1 & 0 \\
0 & 2p & 1
\end{bmatrix} \tag{4}
\]
维护一个操作矩阵的前缀积就可以了。
#define LOCAL0
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int n, m, q;
ll op[200005][3][3];
struct node
{
ll x, y, z;
}a[200005];
ll ma[3][3];
void mul(int t){
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
for(int k=0; k<3; k++){
op[t][i][j] += op[t-1][i][k]*ma[k][j];
}
}
}
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
op[0][0][0] = op[0][1][1] = op[0][2][2] = 1;
cin >> n;
for(int i=1; i<=n; i++){
cin >> a[i].x >> a[i].y;
a[i].z = 1;
}
int _op, p;
cin >> m;
for(int i=1; i<=m; i++){
cin >> _op;
memset(ma, 0, sizeof(ma));
if(_op==1){
ma[0][1] = -1; ma[1][0] = 1; ma[2][2] = 1;
}
else if(_op==2){
ma[0][1] = 1; ma[1][0] = -1; ma[2][2] = 1;
}
else if(_op==3){
cin >> p;
ma[0][0] = -1; ma[1][1] = 1; ma[2][2] = 1; ma[2][0] = 2*p;
}
else if(_op==4){
cin >> p;
ma[0][0] = 1; ma[1][1] = -1; ma[2][2] = 1; ma[2][1] = 2*p;
}
mul(i);
}
cin >> q;
for(int i=1; i<=q; i++){
cin >> p >> _op;
cout << a[_op].x*op[p][0][0]+a[_op].y*op[p][1][0]+a[_op].z*op[p][2][0] << " " << a[_op].x*op[p][0][1]+a[_op].y*op[p][1][1]+a[_op].z*op[p][2][1] << endl;
}
return 0;
}

浙公网安备 33010602011771号