有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开.你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态.对于任意一个开关,最多只能进行一次开关操作/算有多少种可以达到指定状态的方法?(不计开关操作的顺序)
分析:设(x_1=1(0))表示(没)开了这个开关,(a_{i,j}=1(0))表示(i,j)这两个开关有(无)联系,则(a_{i,i}=1).
(a_{1,1}x_1xora_{1,2}x_2xor...xora_{1,n}x_n=)初状态xor末状态(很好理解:只有同时满足 开关j影响i 且 j开了 这两个条件,才会对i的状态产生影响)
以此类推可以得到n个这样的方程,构成异或方程组.然后直接高斯消元即可.
我们还可以把一个方程状态压缩为一个int型整数.
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline LL read(){
LL s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
int a[105];
int main(){
int T=read();
while(T--){
int n=read();
for(int i=1;i<=n;i++)a[i]=read();
//第i个开关的初状态
for(int i=1;i<=n;i++){
int j=read();//读入第i个开关的末状态
a[i]^=j;//方程组的右边:初状态xor末状态
a[i]|=1<<i;//状态压缩:a[i][i]=1
}
while(1){
int x=read(),y=read();//x影响y
if(x==0&&y==0)break;
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[i],a[j]);
//找到最大的一个a[i],即主元位数最高的a[i]
if(a[i]==0){ans=1<<(n-i+1);break;}
//0=0,消元完毕,有i-1个主元,n-i+1个自由元
if(a[i]==1){ans=0;break;}
//1=0,方程无解
//用a[i]去消其它方程
for(int k=n;k;k--)
if((a[i]>>k)&1){//从高位到低位,找为1的那一位
for(int j=1;j<=n;j++)
if(i!=j&&((a[j]>>k)&1))a[j]^=a[i];
//如果第j个方程的这一位也是1,则可以把它消去
break;
}
}
if(ans==0)puts("Oh,it's impossible~!!");
else printf("%d
",ans);
}
return 0;
}