对于式子(((A^B)^B)^B)……,即A对B进行连续异或时,可以发现有如下规律:
若B为1,式子的值会在每次异或之后取反,因为0^1=1,1^1=0,……
若B为0,式子的值会保持A值不变,因为0^0=0,1^0=1,……
因而这种性质可以利用在POJ1830这样的有关联的开关问题上:(每个开关只能改变一次)
假设开关1和开关2、3相关联,且开关1的初始状态为0,终态为1,。
用1表示该开关被改变过,0表示未被改变。
则开关1到达终态的情况可能有:①只改变开关1 ②改变开关1、2、3
则对应的异或式子为 ①((1^0)^0)=1 ②((1^1)^1)=1。
同样的对于开关2和开关3可以列出相应的式子,此时将开关是否被操作过设置为变量x1,x2,x3。
列出3条方程组,等式右边1表示与初始状态不同,0表示与初始状态相同。
解的数目即为到达终态的情况数目,每组解对应着一个开关操作情况。
对于题目的第一个测试用例列出的异或方程为
①x1^x2^x3=1
②x1^x2^x3=1
③x1^x2^x3=1
可求出自由变量有2个,则情况总数为1<<2=4。
对于求解异或方程组,和普通的加减方程组是一样的,只需要将加减操作换成异或操作,高斯消元化简后求出自由变量的数目。
第二个测试用例列出的矩阵为
1 1 0 1 1 1 0 1
1 1 0 0 → 0 0 0 1 可看到第二行为无解的情况。
0 0 1 1 0 0 1 1
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int A[35][35]; int B[35]; int n; int ans; void swap(int a,int b) { int i; int temp; for (i=0;i<=n-1;i++) { temp=A[a][i]; A[a][i]=A[b][i]; A[b][i]=temp; } temp=B[a]; B[a]=B[b]; B[b]=temp; } void gauss() { int i,j,k; for (i=0;i<=n-1;i++) { bool flag=false; if (!A[i][i]) { for (j=i+1;j<=n-1;j++) { if (A[j][i]) { swap(i,j); flag=true; break; } } if (!flag) continue; } for (j=i+1;j<=n-1;j++) { if (A[j][i]) { for (k=i;k<=n-1;k++) A[j][k]^=A[i][k]; B[j]^=B[i]; } } } for (i=n-1;i>=0;i--) { if (A[i][i]==0 && B[i]==1) { ans=-1; break; } else if (A[i][i]==0) ans=ans<<1; else { for (j=i-1;j>=0;j--) { if (A[j][i]) { A[j][i]^=A[i][i]; B[j]^=B[i]; } } } } } int main() { int T; scanf("%d",&T); while (T--) { ans=1; scanf("%d",&n); int i; int s[35],e[35]; for (i=0;i<=n-1;i++) scanf("%d",&s[i]); for (i=0;i<=n-1;i++) { scanf("%d",&e[i]); if (e[i]!=s[i]) B[i]=1; else B[i]=0; } memset(A,0,sizeof(A)); for (i=0;i<=n-1;i++) A[i][i]=1; while (1) { int a,b; scanf("%d%d",&a,&b); if (!a && !b) break; A[b-1][a-1]=1; } gauss(); if (ans!=-1) printf("%d\n",ans); else printf("Oh,it's impossible~!!\n"); } return 0; }
异或方程组的高斯消元模板:
//有equ个方程,var个变元。增广矩阵行数为equ,列数为var+1,分别为0到var int equ,var; int a[MAXN][MAXN]; //增广矩阵 int x[MAXN]; //解集 int free_x[MAXN];//用来存储自由变元(多解枚举自由变元可以使用) int free_num;//自由变元的个数 //返回值为-1表示无解,为0是唯一解,否则返回自由变元个数 int Gauss() { int max_r,col,k; free_num = 0; for(k = 0, col = 0 ; k < equ && col < var ; k++, col++) { 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(a[max_r][col] == 0) { k--; free_x[free_num++] = col;//这个是自由变元 continue; } if(max_r != k) { for(int j = col; j < var+1; j++) swap(a[k][j],a[max_r][j]); } for(int i = k+1;i < equ;i++) { if(a[i][col] != 0) { for(int j = col;j < var+1;j++) a[i][j] ^= a[k][j]; } } } for(int i = k;i < equ;i++)//进入此循环的条件:本身矩阵行大于列 或者 因为出现自由变元后使得非自由变元数比行数小 if(a[i][col] != 0)//若等号右边是1则无解,因为等号左边已经消为0 return -1;//无解 if(k < var) return var-k;//自由变元个数 //唯一解,回代 for(int i = var-1; i >= 0;i--) { x[i] = a[i][var]; for(int j = i+1;j < var;j++) x[i] ^= (a[i][j] && x[j]); } return 0; }