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;
}
 
posted @ 2021-01-24 12:09  DinoMax  阅读(186)  评论(0)    收藏  举报