开关问题(异或线性方程组
# 题意
$n$个相同的开关,每个开关都与某些开关有联系,每次操作某个开关,与操作开关相关的开关也会发生变化,即这些相联系的开关状态原来是开就为关,关变开,经过若干次开关操作后使得最后$n$个开关达到一个特定的状态,对于任意一个开关,最多只能拿进行一次开关操作,计算可能达到指定状态的方法,
# 题解
开关只有开和关两个状态,用$0$和$1$来表示,其中$1$表示开,$0$表示关,如果某个开关操作xi = 1,不操作xi = 1
如果两个开关i,j相连那么a[i][j] = 1,否则为0,其中xi为未知数,如果$i$和$j$相关那么xj为真的话显然xi的状态会不变化
而如果开关原来是关即0,操作开关即1,变成1,原来是0,操作,变为1 ,反之亦然,即异或操作
可以得到方程组
a11&x1 ^ a12 & x2 ^ ...... a1n & xn ^ ori 1 = end1
a21&x1 ^ a22 & x2 ^ ...... a2n & xn ^ ori 2 = end2
......
an1&x1 ^ an2 & x2 ^ ...... ann & xn ^ ori n = endn
根据异或的性质可以将每个ori 移到方程的右边
高斯消元求异或方程组即可
最后因为x的取值稚嫩那个是0或1,最后方案数就是自由元个数k,2k
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 const int N=110; 5 int n; 6 int a[N][N]; 7 int gauss() 8 { 9 int c, r; 10 for (c = 1, r = 1; c <= n; c ++ ){ 11 int t = r; 12 if(a[r][c] != 1) 13 for (int i = r + 1; i <= n; i ++ ) 14 if (a[i][c]) 15 t = i; 16 if (!a[t][c]) continue; 17 for (int i = c; i <= n + 1; i ++ ) swap(a[t][i], a[r][i]); 18 19 for (int i = r + 1; i <= n; i ++ ) 20 if(a[i][c]) 21 for (int j = n+1; j >= c; j -- ) 22 a[i][j] ^= a[r][j]; 23 r ++ ; 24 } 25 int res=1; 26 if (r < n+1){ 27 for (int i = r; i <= n; i ++ ) 28 { 29 if (a[i][n+1]) return -1; 30 res*=2; 31 } 32 } 33 return res; 34 } 35 int main(){ 36 int t; 37 scanf("%d",&t); 38 while(t--){ 39 memset(a,0,sizeof a); 40 scanf("%d",&n); 41 for(int i=1;i<=n;i++) 42 cin>>a[i][n+1]; 43 for(int i=1;i<=n;i++){ 44 int tmp; 45 cin>>tmp; 46 a[i][n+1] ^= tmp; 47 a[i][i]=1; 48 } 49 int x,y; 50 while(scanf("%d%d",&y,&x),x||y) 51 a[x][y]=1; 52 int ans=gauss(); 53 if(ans==-1) puts("Oh,it's impossible~!!"); 54 else printf("%d\n",ans); 55 } 56 }