某些开关的动作可能影响另一些开关的状态,因此以开关为节点,如果存在这种关系就加入一条有向边(开始我想成对称的了,浪费了很多时间- -),这样就构成了一个图,可以用邻接矩阵表示(但是要转置一下,后面细说)。当某个开关按下时,其自身状态改变,受其影响的开关的状态也改变。
用两个N维向量表示初始状态和结束状态,两者逐个元素异或,就得到了开关状态的变化。
以第一个样例输入为例分析,3个开关,两两相连,初始状态000,最终状态111,将对角线的0全部换成1,得矩阵A=
将矩阵每一列想象为一个开关按下后产生的效果(1表示状态翻转,0表示不变),比如,第二列就表示按下第二个开关,则第二个开关的本身状态要改变(这就是把对角线0换成1的原因),受第二个开关影响的开关j状态也要改变,恰好对应邻接矩阵中A[j, 2]=1
把A写成分块矩阵的形式,每一列作为一个子矩阵,则有A=[a1, a2, a3],此处ai均为列向量,设第i个开关按下次数为xi,xi=0或1(开关按两下和没按是等效的,0/1就够了)
记初始状态b0=[0,0,0],最终状态b1=[1,1,1],则状态变化b=b0^b1=[1,1,1],这里b也是列向量。目标就是求x1a1 + x2a2 +x3a3 = b的解的个数(此处的加是模2加,也就是异或,下同)
这个方程可以写成
下面就是解这个线性方程组
对增广矩阵[A b]做初等行变换,化成阶梯形(高斯消元法),如果存在[0,0,…,0,1]的行,就是无解;如果存在r行[0,0,…,0,0],就意味着有r个自由变量,因为这里的变量只取0/1,所以有2r个解;如果不存在[0,0,…,0,*],即把最后一行去掉后不存在全0行,则A为满秩矩阵,则方程组有唯一解。
//
// main.cpp
// HDU-Fighting
//
// Created by 绿色健康文艺小清新 on ...
// Copyright 2016年 绿色健康文艺小清新. All rights reserved.
//
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int mod = 2;
int a[30][31];
int gcd(int a, int b){
int t;
while(b){
t = b;
b = a % b;
a = t;
}
return a;
}
int lcm(int a, int b){
return a / gcd(a, b) * b;
}
int Gauss(int equ, int var){
int k, col;
for (k = 0, col = 0; k < equ && col < var; ++k, ++col){
int max_r = k;
for (int i = k + 1; i < equ; ++i){
if (abs(a[i][col]) > abs(a[max_r][col])) max_r = i;
}
if (max_r != k){
for (int i = k; i <= var; ++i)
swap(a[k][i], a[max_r][i]);
}
if (a[k][col] == 0){
--k;
continue;
}
for (int i = k + 1; i < equ; ++i){
if (a[i][col] != 0) {
int LCM = lcm(abs(a[i][col]), abs(a[k][col]));
int ta = LCM / abs(a[i][col]);
int tb = LCM / abs(a[k][col]);
if (a[i][col] * a[k][col] < 0) tb = -tb;
for (int j = col; j <= var; ++j) {
a[i][j] = ((a[i][j] * ta - a[k][j] * tb) % mod + mod) % mod;
}
}
}
}
for (int i = k; i < equ; ++i){
if (a[i][col] != 0) return -1;
}
return var - k;
}
int s[30], e[30];
long long int b[30];
void build(int n)
{
int x, y;
while(scanf("%d%d", &x, &y), x || y)
a[y - 1][x - 1] = 1;
for(int i = 0; i < n; i++)
a[i][n] = s[i] ^ e[i];
}
void init()
{
b[0] = 1;
for(int i = 1; i < 30; i++)
b[i] = b[i - 1] * 2;
}
int main(void)
{
int t, n;
scanf("%d", &t);
init();
while(t--)
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
for(int j = 0; j <= n; j++)
a[i][j] = 0;
for(int i = 0; i < n; i++)
{
scanf("%d", &s[i]);
a[i][i] = 1;
}
for(int i = 0; i < n; i++)
scanf("%d", &e[i]);
build(n);
int ans = Gauss(n, n);
if(ans == -1)
printf("Oh,it's impossible~!!\n");
else
printf("%lld\n", b[ans]);
}
return 0;
}