CF1458C Latin Square
題目來源:Codeforces, Codeforces Round #691 (Div. 1), CF#691, CF1458C Latin Square
本題題解
發現 \(\texttt{RLDU}\) 和 \(\texttt{IC}\) 是完全不同的兩類操作,將它們同時維護很困難。
本題的突破口是:初始時的每個格子,最終都會恰好對應答案里的一個格子。於是我們拆開來考慮每個格子對答案的“貢獻”。
把一個格子看成三元組 \((i,j,a_{i,j})\),考慮一次操作會對這個三元組產生什么樣的影響:
- \(\texttt{R}\):\((x, y, z) \to (x,y + 1, z)\)。注意,這里的加法是在 \(\bmod n\) 意義下的,下同。
- \(\texttt{L}\):\((x, y, z) \to (x, y - 1, z)\)。注意,這里的減法是在 \(\bmod n\) 意義下的,下同。
- \(\texttt{D}\):\((x, y, z) \to (x + 1, y, z)\)。
- \(\texttt{U}\):\((x, y, z)\to (x - 1, y, z)\)。
- \(\texttt{I}\):\((x, y, z)\to (x, z, y)\)。即交換 2, 3 兩項。
- \(\texttt{C}\):\((x, y, z)\to (z, y, x)\)。即交換 1, 3 兩項。
發現轉化后,這 \(6\) 種對三元組進行的操作,是“可合並”的。
具體地,我們遍歷要進行的 \(m\) 個操作,就能預處理出:依次進行這些操作后,任意一個初始三元組會產生什么變化。
然后枚舉每個格子,將預處理出的變化作用於這個格子上,就能知道這個格子最終對答案的貢獻是什么。
時間復雜度 \(\mathcal{O}(m + n^2)\)。
參考代碼
建議使用快速輸入、輸出,詳見本博客公告。
// problem: CF1458C
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
const int MAXN = 1000;
const int MAXM = 1e5;
int n, m;
int a[MAXN + 5][MAXN + 5];
char s[MAXM + 5];
int ord[4], val[4], res[4];
int ans[MAXN + 5][MAXN + 5];
void solve_case() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
cin >> a[i][j];
}
}
cin >> (s + 1);
ord[1] = 1;
ord[2] = 2;
ord[3] = 3; // 三元組 (i, j, a[i][j])
val[1] = val[2] = val[3] = 0;
for (int i = 1; i <= m; ++i) {
if (s[i] == 'R') {
val[ord[2]]++;
} else if (s[i] == 'L') {
val[ord[2]]--;
} else if (s[i] == 'D') {
val[ord[1]]++;
} else if (s[i] == 'U') {
val[ord[1]]--;
} else if (s[i] == 'I') {
swap(ord[2], ord[3]);
} else if (s[i] == 'C') {
swap(ord[1], ord[3]);
} else {
assert(0);
}
}
// cerr << "changes in val: " << val[1] << " " << val[2] << " " << val[3] << endl;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
// (i, j, a[i][j])
for (int k = 1; k <= 3; ++k) {
if (ord[k] == 1) {
res[k] = i + val[1];
} else if (ord[k] == 2) {
res[k] = j + val[2];
} else if (ord[k] == 3) {
res[k] = a[i][j] + val[3];
}
res[k] = (res[k] % n + n) % n;
if (res[k] == 0)
res[k] = n;
}
// cerr << "** " << res[1] << " " << res[2] << " " << res[3] << endl;
ans[res[1]][res[2]] = res[3];
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
cout << ans[i][j] << " \n"[j == n];
}
}
}
int main() {
int T; cin >> T; while (T--) {
solve_case();
}
return 0;
}