P10499 解題報告

Brilliant11001發表於2024-09-11

更好的閱讀體驗

題目傳送門

題目大意:

\(n\) 個開關,\(0\) 表示關,\(1\) 表示開,每個開關還有帶動的開關,若操作一個開關,那麼它帶動的開關也會相應變換。

現給出這 \(n\) 個開關的初始狀態 \(s_i\) 和末狀態 \(t_i\),詢問有多少種方法能將初始態轉變為末態(不考慮操作先後順序且每個開關至多操作一次)。

思路:

高斯消元解異或方程組經典題。

先考慮將原題抽象成方程組。

\(x_i\) 表示第 \(i\) 個開關的操作次數,因為每個開關至多操作一次,所以 \(x_i = 0\)\(x_i = 1\)

\(a_{i, j}\) 表示第 \(i\) 個開關和第 \(j\) 個開關之間的聯絡,若 \(a_{i, j} = 1\),則表示操作 \(j\) 會帶動 \(i\),若 \(a_{i, j} = 0\) 表示無影響,特別的,因為操作自己就相當於帶動自己,所以 \(a_{i, i} = 1\)

再根據操作效果:\(0\)\(1\)\(1\)\(0\),和異或一模一樣。

所以可以列出以下方程組:

\[\left\{\begin{matrix} a_{1, 1}x_1 \operatorname{xor} a_{1, 2}x_2 \operatorname{xor} \cdots \operatorname{xor} a_{1, n}x_n = t_1\operatorname{xor} s_1\\ a_{2, 1}x_1 \operatorname{xor} a_{2, 2}x_2 \operatorname{xor} \cdots \operatorname{xor} a_{2, n}x_n = t_2\operatorname{xor} s_2\\ \cdots\\ a_{n, 1}x_1 \operatorname{xor} a_{n, 2}x_2 \operatorname{xor} \cdots \operatorname{xor} a_{n, n}x_n = t_n\operatorname{xor} s_n \end{matrix}\right.\]

異或其實就是不進位加法,所以也可以用高斯消元來解,將加減法換為異或就行了。

這道題要求操作方案數,那麼找自由元的數量就好了。因為若某個未知數是自由元,那麼它取 \(0\)\(1\) 都可以,於是貢獻了兩種方案,根據乘法原理,應該把自由元的數量這麼多 \(2\) 乘起來,即 \(2^{cnt}\)\(cnt\) 為自由元的數量。

同時由於係數只能為 \(0\)\(1\),所以一個行向量可以壓縮為一個二進位制整數或者用 bitset 來操作,這樣就能一次異或一整行,時間複雜度降低為 \(O(\frac{n^3}{\omega})\),寫起來也方便許多。

\(\texttt{Code:}\)

#include <cmath>
#include <bitset>
#include <iostream>

using namespace std;

const int N = 35;

int T;
int n;
bitset<N> a[N];
int ans;

int gauss() {
    int c, r;
    for(c = 0, r = 0; c < n; c++) {
        int t = r;
        for(int i = r + 1; i < n; i++)
            if(a[i][c]) {
                t = i;
                break;
            }
        
        if(!a[t][c]) continue;

        if(t != r) swap(a[t], a[r]);

        for(int i = r + 1; i < n; i++)
            if(a[i][c])
                a[i] = a[i] ^ a[r];
        ++r;
    }
    if(r < n) {
        for(int i = r; i < n; i++) {
            if(a[i][n])
                return -1;
        }
        ans = 1 << n - r;
        return 0;
    }
    return 1;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> T;
    while(T--) {
        cin >> n;
        ans = 1;
        int x;
        for(int i = 0; i < n; i++) 
            a[i].reset(), a[i].set(i, 1);
        for(int i = 0; i < n; i++) {
            cin >> x;
            a[i][n] = x;
        }
        for(int i = 0; i < n; i++) {
            cin >> x;
            a[i][n] = a[i][n] ^ x;
        }
        int y;
        while(cin >> x >> y && x && y)
            a[y - 1].set(x - 1, 1);
        int type = gauss();
        if(type >= 0) cout << ans << '\n';
        else cout << "Oh,it's impossible~!!" << '\n';
    }
    return 0;
}

相關文章