1 条题解

  • 0
    @ 2025-8-24 21:35:21

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar LJC00118
    Alice foo foo!

    搬运于2025-08-24 21:35:21,当前版本为作者最后更新于2020-10-27 14:26:59,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    前言

    今天下午同学 YLWang 突然告诉我有这样一个帖子,于是思考了一下此题不打表的做法,就有了这篇题解。


    前置知识:高斯消元,burnside 引理

    我们将 1 1 看成 (1)0 (-1)^0 1 -1 看成 (1)1 (-1)^1 ,我们求两个数的乘积就可以变成幂次相加的性质,又因为有 (1)2=(1)0 (-1)^2 = (-1)^0 ,所以我们的幂次相加是在 mod2 \mod 2 意义下进行的,容易发现这就是异或,我们可以将题目转换为在每个位置上填上 0/1 0/1 ,使得每个数等于相邻的四个数的异或和,求本质不同的方案数。

    如果不用考虑本质不同的限制,我们可以对于每个位置 (i,j) (i, j) 列出式子 $ x_{i, j} \oplus x_{i - 1, j} \oplus x_{i + 1, j} \oplus x_{i, j - 1} \oplus x_{i, j + 1} = 0 $,其中 \oplus 是异或,我们求出这个方程组的解的个数就行了,方程组解的个数是 2 2 的方程组自由元个数次幂,可以用高斯消元 O(n6) O(n^6) 求出,由于是用高斯消元求解异或方程组,可以用 bitset 将复杂度优化至 O(n6w) O(\frac{n^6}{w})

    如果要求本质不同的方案数,我们可以用 burnside 引理来解决,容易发现只有翻转和旋转的置换群大小是常数级别的(小于等于 8 8 ),我们对于每种置换群把不动点压成一个变量,求一下方案数即可。

    复杂度 O(n6w) O(\frac{n^6}{w}) ,实际上 n=100 n=100 也能在 1 1 秒之内跑出解。

    #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:可以将此做法优化至 O(n4w) O(\frac{n^4}{w}) ,感谢 @142857cs 的提点

    我们只把第一行设为未知数,剩下的格子可以通过第一行表示出来,这样就只有 n2 n^2 个方程和 n n 个系数了

    code

    • 1

    信息

    ID
    1224
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者