这道题是个十分有趣的数学题。有n个开关,这n个开关两两之间可能有着关系,如果有关系,按下这个开关,另一个开关的状态也会改变。先给你n个开关的初始状态,再给你n个开关的最终状态,然后告诉你开关之间的关系,问你能不能通过调整开关使达到最终状态,如果能,求方案的数量。
我们用0,1分别表示开关的两个不同状态,对于每个开关,我们知道其他的n-1个开关与它的关系。这个开关是通过哪个状态变成了最终状态,这个设为未知量xi。那么就有n个未知量,关于未知量的方程就需要用到我们之前储存的关系来建立增广矩阵。假如按了i,j也会随着变化,那我们就把a【j】【i】的系数存为1,否则为0。第i个方程,存的a【i】【j】=1代表按j会改变i,每个方程的常数项是第i个开关的初始状态与最终状态的异或值。然后我们就得到了n个关于n个开关的方程组。异或运算其实就是不进位的加法运算,我们仍然用高斯消元解出增广矩阵。消除完之后,剩下的自由元的数量为k,那么ans=2^k。如果存在0=1这种情况,则无解。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,t,ans,a[40];
int main()
{
scanf("%d",&t);
while(t--){
memset(a,0,sizeof(a));
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,j;i<=n;i++){
scanf("%d",&j);
a[i]^=j;
a[i]|=(1<<i);// a[i][i]=1
}
int x,y;
while(~scanf("%d%d",&x,&y)&&x&&y){
a[y]|=(1<<x);
}
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]);
if(a[i]==0){
ans=1<<(n-i+1);break;
}
if(a[i]==1){
ans=0;break;
}
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) printf("Oh,it's impossible~!!\n");
else printf("%d\n",ans);
}
return 0;
}