题目大意:给出n个开关的初始状态以及目标状态,按下一个开关会相应改变其它某些开关的状态(包括自己),问有多少种方案达到目标状态(每个开关最多按一次)。
这道题目是高斯消元求自由变元的模板题。
首先我们可以列出每个开关i的方程,(A1*t1+A2*t2+A3*t3...+An*tn) mod 2=Bi
其中当改变了第j个开关也会改变第i个开关的状态时,Aj=1,否则=0。当第i个开关的初始状态等于结束状态时,Bi=0,否则=1。
ti表示第i个开关最后有没有按,作为方程中的变量。
我们只要求解出这n个方程的自由变元数量t,答案就是2^t了。
自由变元并不是指值不确定的变量数,而是指不确定的“联通块”数。
比如x+y=2,这个时候自由变元只有1个,因为x和y是互相联系的,属于一个“联通块”,只要确定了其中一个,就可以确定另一个。
再比如x+y+z=2,自由变元就有2个。
我是这样求自由变元数的:
一开始和高斯消元步骤一样。但是往回代的时候需要判断一些东西:
如果当前需要求解的这个变量的系数为0,那么有可能增加自由变元,也有可能无解;
否则这个变量就不可能增加自由变元,虽然它可以是任意值,但是它可以由其它自由变量推出,并没有增加方案数。
#include
#include
#include
#include
#define mem(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for (i=j;i<=k;i++)
#define down(i,j,k) for (i=j;i>=k;i--)
using namespace std;
const int N=30;
int K,n,i,j,u,v,t,fs[N+5],ls[N+5],a[N+5][N+5],c[N+5];
void Swap(int u,int v)
{
int i;
rep(i,1,n+1) swap(a[u][i],a[v][i]);
}
void outit()
{
int i,j;
rep(i,1,n)
{
rep(j,1,n+1) printf("%d ",a[i][j]);
printf("\n");
}
printf("\n");
}
int Gauss()
{
int i,j,k,ret=0;
rep(i,1,n-1)
{
rep(j,i,n)
if (a[j][i]) break;
if (j>n) continue;
Swap(i,j);
rep(j,i+1,n)
{
if (!a[j][i]) continue;
rep(k,i,n+1) a[j][k]^=a[i][k];
}
}
// outit();
down(i,n,1)
{
rep(j,i+1,n)
{
if (!a[i][j]) continue;
if (c[j]==-1) {a[i][n+1]=-1; break;}
else a[i][n+1]^=c[j];
}
if (!a[i][i]) {
if (a[i][n+1]) return -1;
ret++; c[i]=-1;
}
else c[i]=a[i][n+1];
}
return ret;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("button.in","r",stdin);
freopen("button.out","w",stdout);
#endif
scanf("%d",&K);
while (K--)
{
mem(a); mem(c);
scanf("%d",&n);
rep(i,1,n) scanf("%d",&fs[i]);
rep(i,1,n) scanf("%d",&ls[i]);
rep(i,1,n) a[i][n+1]=fs[i]^ls[i],a[i][i]=1;
while (1)
{
scanf("%d%d",&u,&v);
if (u==0 && v==0) break;
a[v][u]=1;
}
// outit();
t=Gauss();
if (t==-1) printf("Oh,it's impossible~!!\n");
else printf("%d\n",(int)pow(2.0,t));
}
return 0;
}