1 条题解
-
0
自动搬运
来自洛谷,原作者为

LJC00118
Alice foo foo!搬运于
2025-08-24 21:35:21,当前版本为作者最后更新于2020-10-27 14:26:59,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
前言
今天下午同学 YLWang 突然告诉我有这样一个帖子,于是思考了一下此题不打表的做法,就有了这篇题解。
前置知识:高斯消元,burnside 引理
我们将 看成 , 看成 ,我们求两个数的乘积就可以变成幂次相加的性质,又因为有 ,所以我们的幂次相加是在 意义下进行的,容易发现这就是异或,我们可以将题目转换为在每个位置上填上 ,使得每个数等于相邻的四个数的异或和,求本质不同的方案数。
如果不用考虑本质不同的限制,我们可以对于每个位置 列出式子 $ x_{i, j} \oplus x_{i - 1, j} \oplus x_{i + 1, j} \oplus x_{i, j - 1} \oplus x_{i, j + 1} = 0 $,其中 是异或,我们求出这个方程组的解的个数就行了,方程组解的个数是 的方程组自由元个数次幂,可以用高斯消元 求出,由于是用高斯消元求解异或方程组,可以用 bitset 将复杂度优化至 。
如果要求本质不同的方案数,我们可以用 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:可以将此做法优化至 ,感谢 @142857cs 的提点
我们只把第一行设为未知数,剩下的格子可以通过第一行表示出来,这样就只有 个方程和 个系数了
- 1
信息
- ID
- 1224
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者