题解 P2225 【[HNOI2001]棋盘变换】
前言
今天下午同学 YLWang 突然告诉我有这样一个帖子,于是思考了一下此题不打表的做法,就有了这篇题解。
前置知识:高斯消元,burnside 引理
我们将
如果不用考虑本质不同的限制,我们可以对于每个位置
如果要求本质不同的方案数,我们可以用 burnside 引理来解决,容易发现只有翻转和旋转的置换群大小是常数级别的(小于等于
复杂度
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
using namespace std;
typedef unsigned long long ull;
typedef pair <int, int> pii;
typedef long long ll;
template <typename _T>
inline void read(_T &f) {
f = 0; _T fu = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') { fu = -1; } c = getchar(); }
while (c >= '0' && c <= '9') { f = (f << 3) + (f << 1) + (c & 15); c = getchar(); }
f *= fu;
}
template <typename T>
void print(T x) {
if (x < 0) putchar('-'), x = -x;
if (x < 10) putchar(x + 48);
else print(x / 10), putchar(x % 10 + 48);
}
template <typename T>
void print(T x, char t) {
print(x); putchar(t);
}
const int N = 35;
const int dx[4] = {1, -1, 0, 0};
const int dy[4] = {0, 0, 1, -1};
bitset <N * N> mat[N * N];
int gauss(int n, int m) {
int ans = 1;
for (int i = 1; i <= m; i++) {
int id = 0;
for (int j = i; j <= n; j++) {
if (mat[j][i] == 1) {
id = j;
break;
}
}
if (!id) {
ans <<= 1;
continue;
}
if (id != i) swap(mat[i], mat[id]);
for (int j = i + 1; j <= n; j++) {
if (mat[j][i]) {
mat[j] ^= mat[i];
}
}
}
return ans;
}
int go[N * N], used[N * N], id[N * N];
int n, len, tot, ans;
struct Matrix { int a[N][N]; };
bool operator < (const Matrix a, const Matrix b) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (a.a[i][j] != b.a[i][j]) {
return a.a[i][j] < b.a[i][j];
}
}
}
return 0;
}
set <Matrix> states;
set <Matrix> :: iterator it;
queue <Matrix> q;
Matrix rotate(Matrix a) {
Matrix ans;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
ans.a[j][n - i + 1] = a.a[i][j];
}
}
return ans;
}
Matrix flip(Matrix a) {
Matrix ans;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
ans.a[n - i + 1][j] = a.a[i][j];
}
}
return ans;
}
inline int calc(int x, int y) {
return (x - 1) * n + y;
}
int main() {
read(n);
Matrix a;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
a.a[i][j] = calc(i, j);
}
}
states.insert(a); q.push(a);
while (!q.empty()) {
Matrix u = q.front(), v; q.pop();
v = rotate(u);
if (!states.count(v)) {
states.insert(v);
q.push(v);
}
v = flip(u);
if (!states.count(v)) {
states.insert(v);
q.push(v);
}
}
for (it = states.begin(); it != states.end(); ++it) {
Matrix u = *it;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
go[u.a[i][j]] = calc(i, j);
}
}
memset(used, 0, sizeof(used)); tot = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int now = calc(i, j);
if (!used[now]) {
++tot;
while (!used[now]) {
used[now] = 1;
id[now] = tot;
now = go[now];
}
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int now = calc(i, j);
mat[now].reset(); mat[now][id[now]] = 1;
for (int k = 0; k < 4; k++) {
int x = i + dx[k], y = j + dy[k];
if (x <= 0 || y <= 0 || x > n || y > n) continue;
int v = id[calc(x, y)];
mat[now][v] = 1 - mat[now][v];
}
}
}
ans += gauss(n * n, tot);
}
ans /= (int)states.size();
print(ans, '\n');
return 0;
}
upd:可以将此做法优化至
我们只把第一行设为未知数,剩下的格子可以通过第一行表示出来,这样就只有
code