Luogu P2447 [SDOI2010]外星千足虫
题意
给定 \(n\) 个变量和 \(m\) 个异或方程,求最少需要多少个才能确定每个变量的解。
\(\texttt{Data Range:}1\leq n\leq 10^3,1\leq m\leq 2\times 10^3\)
题解
高斯消元解异或方程组。
求解这个东西可以直接高斯约旦法,主要问题是第一问。
注意,第一问不等同于求矩阵的秩,因为要求是选一段前缀,矩阵的秩是可以任意选的。
这个时候考虑对选主元过程进行贪心,每一次拿那个位置最前面并且满足条件的当主元来消即可。因为拿靠后的那个方程来消并不能够使答案变得更优,所以贪心策略是正确的。
但是暴力校园是 \(O(n^3)\) 的,感觉跑不过(但是为什么 \(\textsf{t\color{red}ommy0103}\) 就跑过去了),于是可以考虑一下 bitset 优化,这下复杂度就变成了 \(O(\frac{n^3}{\omega})\),实测开 O2 跑得飞快。
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=1e3+51;
bitset<MAXN>mat[MAXN*2];
ll n,m,pivot,mx;
ll v[MAXN*2],id[MAXN*2];
char ch[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
int main()
{
n=read(),m=read(),id[m+1]=m+1;
for(register int i=1;i<=m;i++)
{
scanf("%s",ch+1),v[i]=read(),id[i]=i;
for(register int j=1;j<=n;j++)
{
mat[i][j]=ch[j]-'0';
}
}
for(register int i=1;i<=n;i++)
{
pivot=m+1;
for(register int j=i;j<=m;j++)
{
mat[j][i]&&id[pivot]>id[j]?pivot=j:1;
}
if(pivot==m+1)
{
return puts("Cannot Determine"),0;
}
mx=max(mx,id[pivot]),swap(mat[i],mat[pivot]);
swap(v[i],v[pivot]),swap(id[i],id[pivot]);
for(register int j=1;j<=m;j++)
{
i!=j&&mat[j][i]?mat[j]^=mat[i],v[j]^=v[i]:1;
}
}
printf("%d\n",mx);
for(register int i=1;i<=n;i++)
{
puts(v[i]?"?y7M#":"Earth");
}
}