1. 程式人生 > 實用技巧 >聯賽模擬測試20 D. Drink

聯賽模擬測試20 D. Drink


其實這道題可以當作是一道模擬???
這道題其實本來用字元輸入輸出的選學卡常操作是可以過掉這道題的,但是由於划水之星skyh的存在這種做法立即就被卡成\(95\)了,所以還是讓我們來打正解吧.
題中的部分分應該很好拿,前\(30\)分直接暴力就行了,中間的\(30\)分我們吧所有的旋轉儲存下來,最後再一起旋轉出來就可以了.
然而正解和上面的沒有半點聯絡
我們需要介紹一種新的資料結構: 十字連結串列, 和普通連結串列不同的是我們需要同時記錄上下左右四個方向,這樣我們在旋轉一個正方形的時候只需要處理邊界的一些點就可以了,可以將單次時間複雜度降為\(O(n)\),可能常數比較\(Huge\),但是容易注意到矩形裡面的點旋轉之後方向就會亂掉,如果我們在處理的時候暴力判斷的話會讓碼長到飛起,既然常數已經這麼大了,我們乾脆就不要注意常數了,我們可以發現矩形只會向一個方向旋轉,於是我們只需要確定矩形的一個方向其他的也就知道了,於是我們每次列舉到一個矩形直接把它再暴力轉回來,就可以方便許多,為方便確認一個基準的方向,我們可以在矩形的外側再圍一圈永遠不會被操作的點,以此作為基準的方向,矩形裡面的其他的點就可以一邊走一邊轉回來了.這樣這道問題就被我們解決了.程式碼不算長.時間複雜度為\(\Huge O\)

\((mn)\), 注意到常數有多大了吧

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
const int N = 2e3 + 10;
struct Node {
	int l, r, u, d, num;
	Node() {
		num = -1;
	}
} edge[N * N];
int id[N][N];
void zig(int now) { // 左旋
	int cnt = edge[now].u;
	edge[now].u = edge[now].l;
	edge[now].l = edge[now].d;
	edge[now].d = edge[now].r;
	edge[now].r = cnt;
}
void zag(int now) { // 右旋
	int cnt = edge[now].u;
	edge[now].u = edge[now].r;
	edge[now].r = edge[now].d;
	edge[now].d = edge[now].l;
	edge[now].l = cnt;
}
void swp_down(int from) { //將當前點下面的點轉回來
	int to = edge[from].d;
	if(edge[to].u == from) return;
	else if(edge[to].l == from) zig(to);
	else if(edge[to].r == from) zag(to);
	else if(edge[to].d == from) {
		zig(to); zig(to);
	}
}
void swp_right(int from) { //將當前點右面的點轉回來
	int to = edge[from].r;
	if(edge[to].l == from) return;
	else if(edge[to].d == from) zig(to);
	else if(edge[to].u == from) zag(to);
	else if(edge[to].r == from) {
		zig(to); zig(to);
	}
}
void swp_left(int from) { //將當前點左面的點轉回來
	int to = edge[from].l;
	if(edge[to].r == from) return;
	else if(edge[to].u == from) zig(to);
	else if(edge[to].d == from) zag(to);
	else if(edge[to].l == from) {
		zig(to); zig(to);
	}
}
void swp_up(int from) { //將當前點上面的點轉回來
	int to = edge[from].u;
	if(edge[to].d == from) return;
	else if(edge[to].r == from) zig(to);
	else if(edge[to].l == from) zag(to);
	else if(edge[to].u == from) {
		zig(to); zig(to);
	}
}
int find(int x, int y) {//從起點查詢到當前需要操作的點的編號
	int now = id[x][0];
	while(y--) {
		swp_right(now);
		now = edge[now].r;
	} 
	return now;
}
std::vector<int> L, R, U, D;
void Connect(int l, int d, int r, int u) { //上下左右對應的四個位置分別斷邊和連線新邊
	int L = edge[l].l, D = edge[d].d;
	int R = edge[r].r, U = edge[u].u;
	edge[l].l = U;
	edge[u].u = R;
	edge[r].r = D;
	edge[d].d = L;
	edge[L].r = d;
	edge[U].d = l;
	edge[R].l = u;
	edge[D].u = r;
}
void Rotate(int x, int y, int d) { //旋轉選定矩形
	int now = find(x, y);
	L.clear();
	R.clear();
	U.clear();
	D.clear();
	L.push_back(now);
	for(int i = 1; i < d; ++i) { //圍繞著操作矩形把四周的的點村下來
		swp_left(now);
		swp_down(now);
		now = edge[now].d;
		L.push_back(now);
	}
	swp_left(now);
	swp_down(now);
	D.push_back(now);
	for(int i = 1; i < d; ++i) {
		swp_down(now);
		swp_right(now);
		now = edge[now].r;
		D.push_back(now);
	}
	swp_down(now);
	swp_right(now);
	R.push_back(now);
	for(int i = 1; i < d; ++i) {
		swp_up(now);
		swp_right(now);
		now = edge[now].u;
		R.push_back(now);
	}
	swp_up(now);
	swp_right(now);
	U.push_back(now);
	for(int i = 1; i < d; ++i) {
		swp_up(now);
		swp_left(now);
		now = edge[now].l;
		U.push_back(now);
	}
	swp_up(now);
	swp_left(now);
	for(int i = 0; i < L.size(); ++i) {
		Connect(L[i], D[i], R[i], U[i]);
	}
}
int tot;
int main() {
	//freopen("a.in", "r", stdin);
	freopen("drink.in", "r", stdin);
	freopen("drink.out", "w", stdout);
	int n, m, q;
	scanf("%d%d%d", &n, &m, &q);
	for(int i = 0; i <= m + 1; ++i) { //存一圈無用的節點
		id[0][i] = ++tot;
	}
	for(int i = 1; i <= n; ++i) {
		id[i][0] = ++tot;
		id[i][m + 1] = ++tot;
	}
	for(int i = 0; i <= m + 1; ++i) {
		id[n + 1][i] = ++tot;
	}
	for(int i = 1; i <= n; ++i) {
		for(int j = 1; j <= m; ++j) {
			int x;
			scanf("%d", &x);
			edge[++tot].num = x;
			id[i][j] = tot;
		}
	}
	for(int i = 0; i <= n + 1; ++i) {
		for(int j = 0; j <= m + 1; ++j) { //連邊
			if(i != 0) edge[id[i][j]].u = id[i - 1][j];
			if(i != n + 1) edge[id[i][j]].d = id[i + 1][j];
			if(j != 0) edge[id[i][j]].l = id[i][j - 1];
			if(j != m + 1) edge[id[i][j]].r = id[i][j + 1];
		}
	}
	while(q--) {
		int x, y, d;
		scanf("%d%d%d", &x, &y, &d);
		Rotate(x, y, d);
	}
	int now = 1;
	for(int i = 1; i <= n; ++i) {//輸出答案
		swp_down(now);
		now = edge[now].d;
		swp_right(now);
		for(int j = edge[now].r; ~edge[j].num; j = edge[j].r) {
			printf("%d ", edge[j].num);
			swp_right(j);
		}
		puts("");
	}
	return 0;
}