把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷2447】[SDOI2010] 外星千足虫(异或高斯消元)

点此看题面

  • \(n\)\(01\)变量,给定\(m\)个方程,每个方程给出若干个元素的异或和。
  • 问至少使用前几个方程即可求出所有元素值。
  • \(n\le10^3,m\le2\times10^3\)

线性基+高斯消元

显然,能解出所有元素值的充要条件就是有\(n\)个方程,满足任意一个都无法由其他若干个推得。

发现这个条件有点眼熟,如果我们把每个方程看作一个\(n\)位的二进制数,就变成要满足任意一个数都无法由其他若干个异或得到。

这不就是一个线性基嘛!

所以我们只要维护一个线性基,当元素个数达到\(n\)的时候就说明此时可以解出所有元素了。

由于这时候满足第\(i\)个方程只有第\(i\sim n\)个元素可能有值,因此我们只要执行高斯消元的最后一步操作,从后往前枚举每一个元自之前的方程中消去即可。

具体实现可以用\(bitset\)优化复杂度。

代码:\(O(\frac{n^3}{32})\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
using namespace std;
int n,m,v[N+5];bitset<N+5> s,a[N+5];char st[N+5];
I void Gauss()//轻量版高斯消元
{
	RI i,j;for(i=n;i;--i) for(j=i-1;j;--j) a[j].test(i)&&(a[j]^=a[i],0);//枚举每一个元素从之前的方程中消除
	for(i=1;i<=n;++i) puts(a[i].test(0)?"?y7M#":"Earth");//输出答案
}
int main()
{
	RI i,j,x,t=0;for(scanf("%d%d",&n,&m),i=1;i<=m;++i)
	{
		for(scanf("%s%d",st+1,&x),s.reset(),x&&(s.set(0),0),j=1;j<=n;++j) st[j]&1&&(s.set(j),0);//转化成二进制数
		for(j=1;j<=n;++j) if(s.test(j)) {if(a[j].any()) s^=a[j];else {a[j]=s,++t;break;}}//维护线性基
		if(t==n) return printf("%d\n",i),Gauss(),0;//元素个数达到n说明能解出所有元素了
	}return puts("Cannot Determine"),0;//始终无法
}
posted @ 2021-03-11 20:29  TheLostWeak  阅读(63)  评论(0编辑  收藏  举报