开关问题[POJ1830]

欢迎大家访问我的老师的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=src2xordst2an,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值