欢迎大家访问我的老师的OJ———caioj.cn
题面描述
思路
设 x i x_i xi表示第i个开关的操作情况, x i = 1 x_i=1 xi=1表示按了这个开关, x i = 0 x_i=0 xi=0表示没按。再统计 a i , j a_{i,j} ai,j表示第i个开关和第j个开关的联系情况, a i , j = 1 a_{i,j}=1 ai,j=1表示按下j会影响i的状态, a i , j = 0 a_{i,j}=0 ai,j=0表示不会影响,特别地,令 a i , i = 1 a_{i,i}=1 ai,i=1。
一个开关最后的状态
d
s
t
i
dst_i
dsti,取决于它最初的状态
s
r
c
i
src_i
srci,以及所有与它有关系的开关的操作情况执行异或运算得到的结果,可列出异或方程组:
{
a
1
,
1
x
1
xor
a
1
,
2
x
2
xor
.
.
.
xor
a
1
,
n
x
n
=
s
r
c
1
xor
d
s
t
1
a
2
,
1
x
1
xor
a
2
,
2
x
2
xor
.
.
.
xor
a
2
,
n
x
n
=
s
r
c
2
xor
d
s
t
2
⋮
a
n
,
1
x
1
xor
a
n
,
2
x
2
xor
.
.
.
xor
a
n
,
n
x
n
=
s
r
c
n
xor
d
s
t
n
\begin{cases}a_{1,1}x_1\operatorname{xor}a_{1,2} x_2\operatorname{xor}...\operatorname{xor}a_{1,n}x_n=src_1\operatorname{xor}dst_1\\ a_{2,1}x_1\operatorname{xor}a_{2,2} x_2\operatorname{xor}...\operatorname{xor}a_{2,n}x_n=src_2\operatorname{xor}dst_2\\\vdots\\ a_{n,1}x_1\operatorname{xor}a_{n,2} x_2\operatorname{xor}...\operatorname{xor}a_{n,n}x_n=src_n\operatorname{xor}dst_n\end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧a1,1x1xora1,2x2xor...xora1,nxn=src1xordst1a2,1x1xora2,2x2xor...xora2,nxn=src2xordst2⋮an,1x1xoran,2x2xor...xoran,nxn=srcnxordstn
异或其实就是不进位加法,我们可以通过高斯消元来解决。
在执行高斯消元的过程中,把加、减法替换成异或,且不需要执行乘法。
最终我们可以得到该异或方程组对应的简化阶梯形矩阵。若存在形如 0 = 1 0=1 0=1的方程,则方程组无解。
否则因为自由元可以取 0 0 0或 1 1 1,所以方程解的数量就是 2 自 由 元 数 量 2^{自由元数量} 2自由元数量
由于偷懒省事也省时,我们可以状态压缩一下
a
i
,
j
a_{i,j}
ai,j,即:
int a[100];//0位存 开始xor结束
a[i]^=j;
a[i]|=1<<i;//a[i][i]=1;
a[y]|=1<<x;//按x影响y ,a[y][x]=1
AC code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
int a[100];//0位存 开始xor结束
int main()
{
int t;scanf("%d",&t);
while(t--)
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
int j;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;//按x影响y
int ans=1;
for(int i=1;i<=n;i++)
{
//寻找最高位
int x=i;
for(int j=i+1;j<=n;j++)if(a[j]>a[x])x=j;
swap(a[x],a[i]);
//消元成功
if(a[i]==0){ans=1<<(n-i+1);break;}
//系数全为0,但结果却为1.
if(a[i]==1){ans=0;break;}
for(int j=n-i+1;j>=1;j--)
if((a[i]>>j&1))//a[i][j]==1
{
for(int k=i+1;k<=n;k++)
if(i!=k&&(a[k]>>j&1))//a[k][j]==1
a[k]^=a[i];
break;
}
}
if(ans==0)puts("Oh,it's impossible~!!");
else printf("%d\n",ans);
}
return 0;
}