我们假设是第i个开关的操作情况,为1表示按了这个开关, 为0表示没有按这个开关.再用表示第i个开关和第j个开关有无联系,表示第i个开关和第j个开关有联系,而则表示没有联系,特别的,.(因为进行一次操作就相当于1异或)
一个开关的最后的状态,取决于其初始状态,以及所有跟他有联系的异或操作.列出异或方程组得:
异或就是不进位加法,可以写出增广矩阵,矩阵中每个数要么是1,要么是0.然后,在执行消元的时候就把加法减法替换成异或运算,而且不需要执行乘法操作(无进位).最终我们可以得到对应的简化梯形矩阵.则方程组无解,因为自由元可以取0或1,所以解的数量有,其中为自由元的数量.
在编码时可以用状态压缩把矩阵压缩成二进制的方式表示和运算.其中第0位表示增广矩阵的最后一列常数,第1~n位表示增广矩阵第1~n的系数.由于这道题不需要我们按顺序求出每个开关的操作情况,只要求求方案数,所以我们不用担心顺序的问题.
代码如下:
#include <bits/stdc++.h>
// #define LOCAL
#define INF 0xf3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define debug(a) cout << #a << "=" << a << endl;
using namespace std;
#define vec vector<int>
#define mat vector<vec>
const int N = 50;
int a[N];
void solve(){
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1, x; i <= n; ++i){
cin >> x;
a[i] ^= x;
a[i] |= 1 << i;//a[i][i] = 1
}
int x, y;
while (cin >> x >> y && x && y){
a[y] |= 1 << x; // a[y][x] = 1
}
int ans = 1;
for (int i = 1; i <= n; ++i){
/* 找出主元位数最高的一行,并换到最上面 */
for (int j = i + 1; j <= n; ++j){
if (a[j] > a[i])
swap(a[j], a[i]);
}
/* 如果出现0=1的情况,说明无解 */
if (a[i] == 1){
ans = 0;
break;
}
/* 如果主元全部被消除,当前自由元的个数为n - i + 1,有2^(n-i+1)个解 */
if (a[i] == 0){
ans = 1 << (n - i + 1);
break;
}
/* 将最高位的除了最上面的数全部消除为0 */
for (int k = n; k; --k)
if (a[i] >> k & 1){
for (int j = 1; j <= n; ++j)
if (i != j && (a[j] >> k & 1))
a[j] ^= a[i];
break;
}
}
if (ans == 0)
cout << "Oh,it's impossible~!!\n";
else
cout << ans << '\n';
}
signed main(){
#ifdef LOCAL
freopen("input.in", "r", stdin);
freopen("output.out", "w", stdout);
#endif
IOS;
int k;
cin >> k;
while (k--){
solve();
}
}