1、本题用高斯消元法做,构造方程是难点。用G【i】【j】表示灯i是否被灯j控制,x【i】表示是否按下开关i,则本题相当于解异或方程组:
e[a] = (xa * G[a][a]) ^ (xb * G[a][b]) ^ (xc*G[a][c]) ^ f[a]
e[b] = (xa * G[b][a]) ^ (xb * G[b][b]) ^ (xc*G[b][c]) ^ f[b]
e[c] = (xa * G[c ][a]) ^ (xb * G[c][b]) ^ (xc*G[c][c]) ^ f[c]
2、Gauss函数res返回自由元的个数,则答案就是1<<res,注意,如果存在这样的情况:某一行的1到n列全为0,而第n+1列为1,则输出无解。
3、注意,自己可以控制自己,所以G【i】【i】一定为1。
4、异或运算相当于相加模2。要记住这个结论。为什么两行“相减”的时候也是用的异或运算呢?因为异或运算的逆运算还是异或。打个比方,a+b=c,c-b=a,而a^b=c,c^b=a,这里的异或符号就起到了“减号"的作用。当然,用“相减模2”的思想考虑也是可以的,因为真值表一样,所以“相减模2”和“异或”是等价的。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[30],e[30],G[30][30],ans[30];
bool l[30];
int Gauss(int a[][30],bool l[],int ans[],const int &n){
int res=0,r=1;
for(int i=1;i<=n;i++)
l[i]=false;
for(int i=1;i<=n;i++){
for(int j=r;j<=n;j++)
if(a[j][i]>0){
for(int k=i;k<=n+1;k++)
swap(a[j][k],a[r][k]);
break;
}
if(a[r][i]==0){
res++;
continue;
}
for(int j=1;j<=n;j++)
if(j!=r&&a[j][i]>0){
for(int k=i;k<=n+1;k++)
a[j][k]^=a[r][k];
}
l[i]=true;r++;
}
for(int i=1;i<=n;i++)
if(l[i])
for(int j=1;j<=n;j++)
if(a[j][i]>0) ans[i]=a[j][n+1]^a[j][i];
return res;
}
int main(){
int T,n,num;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&f[i]);
for(int i=1;i<=n;i++)
scanf("%d",&e[i]);
memset(G,0,sizeof(G));
int x,y;
while(scanf("%d%d",&x,&y)==2){
if(x==0&&y==0) break;
G[y][x]=1;
}
for(int i=1;i<=n;i++){
G[i][n+1]=f[i]^e[i];
G[i][i]=1;
}
num=Gauss(G,l,ans,n);
bool flag=true;
for(int i=n;i>n-num;i--)
if(G[i][n+1]!=0){
printf("Oh,it's impossible~!!\n");
flag=false;
break;
}
if(flag){
printf("%d\n",1<<num);
}
}
return 0;
}