题解:P10499 开关问题
前置芝士:高斯消元、异或。
思路
不难发现灯的开关状态与异或有关。设 为灯灭, 为灯开,注意到 ,。所以灯的新状态等于原状态异或 的值。
然后我们可以把灯的状态转化成异或方程组来做。
首先我们要知道什么可以控制一盏灯。能控制它的除了题目中给定的,还有它自己。
很显然每个开关最多被打开一次,设开关是否被打开为 ,若 ,则没有被打开,反之如果被打开,。
然后就是这个题中最关键的方程了:
其中 , 表示 是否可以控制 ,可以为 ,反之为 , 表示开始和结束 的状态是否被改变,是为 ,反之为 。
它的意思是:如果能控制第 盏灯的开关被打开了,它的状态就被改变,由前文可得是异或。
因为 ,所以这个方程一定正确。
对于每一个 列出来方程,然后高斯消元即可。
这个高斯消元还不大一样。因为正常的高斯消元是求和,但是这个是异或,因此我们需要单独写,具体可以看代码。
主要的不同是:
- 不用取模。
- 原有的加减法运算全部改为异或。
如何计算答案?
如果在一个方程中出现了 的情况:
- 如果 ,说明这个方程没有约束,多一个自由元,自由元可以取 或 ,于是答案乘 。
- 反之,等式不成立,无解。
答案显然初始为 。
#include<bits/stdc++.h>
using namespace std;
int s[29],e[29],a[29][30],b[29];
int T,n;
int main(){
int T;
cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)a[i][j]=0;
}
for(int i=1;i<=n;i++)cin>>s[i];
for(int i=1;i<=n;i++)cin>>e[i];
for(int i=1;i<=n;i++)a[i][n+1]=s[i] xor e[i];
while(1){
int x,y;
cin>>x>>y;
if(x==0&&y==0)break;
a[y][x]=1;
}
for(int i=1;i<=n;i++)a[i][i]=1;
for(int i=1;i<=n;i++){
int t=i;
for(int j=i+1;j<=n;j++){
if(a[j][i]>a[t][i])t=j;
}
swap(a[t],a[i]);
if(a[i][i]==0)continue;
for(int j=i+1;j<=n;j++){
int x=a[j][i];
for(int k=i;k<=n+1;k++){
a[j][k]^=(a[i][k]*x);
}
}
}
bool flag=0;
int ans=1;
for(int i=1;i<=n;i++){
int pos=0;
for(int j=1;j<=n;j++){
if(a[i][j]){
pos=j;
break;
}
}
if(pos==0)continue;
for(int j=i+1;j<=n;j++){
if(a[j][pos]){
for(int k=pos;k<=n+1;k++){
a[j][k]^=a[i][k];
}
}
}
}
for(int i=1;i<=n;i++){
int sum=0;
for(int j=1;j<=n;j++){
if(a[i][j]!=0)sum++;
}
if(sum==0){
if(a[i][n+1]==0)ans*=2;
else flag=1;
}
}
if(flag)cout<<"-1"<<endl;
else cout<<ans<<endl;
}
return 0;
}

浙公网安备 33010602011771号