题解:P10499 开关问题

前置芝士:高斯消元、异或。

思路

不难发现灯的开关状态与异或有关。设 00 为灯灭,11 为灯开,注意到 01=10\oplus1=111=01\oplus 1=0。所以灯的新状态等于原状态异或 11 的值。

然后我们可以把灯的状态转化成异或方程组来做。

首先我们要知道什么可以控制一盏灯。能控制它的除了题目中给定的,还有它自己。

很显然每个开关最多被打开一次,设开关是否被打开为 xix_i,若 xi=0x_i=0,则没有被打开,反之如果被打开,xi=1x_i=1

然后就是这个题中最关键的方程了:

(con(1,i)x1)(con(2,i)x2)(con(n,i)xn)=zhuan(i)(\operatorname{con}(1,i)x_1)\oplus(\operatorname{con}(2,i)x_2)\oplus\cdots (\operatorname{con}(n,i)x_n)=\operatorname{zhuan}(i)

其中 i[1,n]i\in[1,n]con(i,j)\operatorname{con}(i,j) 表示 ii 是否可以控制 jj,可以为 11,反之为 00zhuan(i)\operatorname{zhuan}(i) 表示开始和结束 ii 的状态是否被改变,是为 11,反之为 00

它的意思是:如果能控制第 ii 盏灯的开关被打开了,它的状态就被改变,由前文可得是异或。

因为 0x=x0\oplus x=x,所以这个方程一定正确。

对于每一个 ii 列出来方程,然后高斯消元即可。

这个高斯消元还不大一样。因为正常的高斯消元是求和,但是这个是异或,因此我们需要单独写,具体可以看代码。

主要的不同是:

  • 不用取模。
  • 原有的加减法运算全部改为异或。

如何计算答案?

如果在一个方程中出现了 00 0=x0\oplus 0 \oplus\cdots\oplus\ 0=x 的情况:

  • 如果 x=0x=0,说明这个方程没有约束,多一个自由元,自由元可以取 0011,于是答案乘 22
  • 反之,等式不成立,无解。

答案显然初始为 11

#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;
}
posted @   Weslie_qwq  阅读(2)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示