链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2537
浅谈高斯消元
今天才对高斯消元有了一点点深入的理解
对于线性方程组,假设其未知数为
x
1
,
x
2
,
.
.
.
,
x
n
x_1,x_2,...,x_n
x1,x2,...,xn
方程有
m
m
m个
每个变元满足下列情形中的一种:
- 有唯一的解
- 取值不定,比如方程组 x 1 + x 2 = 1 , x 2 + x 3 = 4 x_1+x_2=1,x_2+x_3=4 x1+x2=1,x2+x3=4中的 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3
- 没有解,比如方程组 x 1 = 2 , x 1 = 3 x_1=2,x_1=3 x1=2,x1=3中的 x 1 x_1 x1
高斯消元的作用,其实是把变元之间的依赖关系整理出来
对于每个方程,其中出现的变元,我把它们归到一个集合里去
这样整理下来,我就得到若干个集合,每个集合中包含了一些变元,每个变元只会属于一个集合
对于每个集合,设其中的变元数为
c
n
t
x
cnt_x
cntx
每个方程肯定只与一个集合有关,每个集合与之相关的方程的个数记为
c
n
t
e
cnt_e
cnte
高斯消元之后,肯定有
c
n
t
e
≤
c
n
t
x
cnt_e\leq cnt_x
cnte≤cntx
这
c
n
t
x
cnt_x
cntx个变元都有唯一解当且仅当
c
n
t
x
=
c
n
t
e
cnt_x=cnt_e
cntx=cnte
如果
c
n
t
e
<
c
n
t
x
cnt_e<cnt_x
cnte<cntx,说明这些变量的取值不定,但是只要给出了其中
c
n
t
x
−
c
n
t
e
cnt_x-cnt_e
cntx−cnte个变量的取值,另一些的值就唯一确定了
关于如何判断一个变量是不是无解,在
v
a
n
U
van U
vanU裙里讨论了半天也没有结果(我好菜啊qwq,明年学了线代也许就懂了)
题解
用
x
i
x_i
xi表示第
i
i
i个数字选或不选
把
2
2
2到
500
500
500内的素数都求出来,然后对每个素数列一个异或方程组
高斯消元
用并查集辅助求出我上面所描述的
c
n
t
e
cnt_e
cnte和
c
n
t
x
cnt_x
cntx,答案就是
(
∏
2
c
n
t
x
−
c
n
t
e
)
−
1
(\prod2^{cnt_x-cnt_e})-1
(∏2cntx−cnte)−1(减去都取
0
0
0的情况),其中
x
x
x是枚举每个集合
这道题不会存在无解的情况(一个显然的解就是让所有的变量取
0
0
0)
代码
#include <bits/stdc++.h>
#define maxn 510
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
ll prime[maxn], mark[maxn], ans, N, a[maxn][maxn], f[maxn], cnt_x[maxn], cnt_e[maxn];
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return f*x;
}
void shai()
{
ll i, j;
for(i=2;i<maxn;i++)
{
if(!mark[i])prime[++*prime]=i;
for(j=1;i*prime[j]<maxn and j<=*prime;j++)
{
mark[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
void init()
{
ll i, j, t, x;
cl(a), cl(f), cl(cnt_e), cl(cnt_x);
N=read();
for(i=1;i<=N;i++)
{
x=read();
for(j=1;j<=*prime;j++)
{
t=x;
while(t%prime[j]==0)a[j][i]^=1, t/=prime[j];
}
}
}
void gauss(ll (*a)[maxn], ll n, ll m)
{
ll i, j, k, r, now=1;
for(i=1;i<=n and now<=m;i++)
{
r=now;
for(j=now+1;j<=m;j++)if(a[j][i])r=j;
if(a[r][i]==0)continue;
for(j=1;j<=n+1;j++)swap(a[r][j],a[now][j]);
for(j=1;j<=m;j++)
if(j!=now and a[j][i])for(k=1;k<=n+1;k++)a[j][k]^=a[now][k];
now++;
}
}
ll set_find(ll x){return x==f[x]?x:f[x]=set_find(f[x]);}
void set_merge(ll x, ll y){f[set_find(x)]=set_find(y);}
void work()
{
ll i, j, x, ans=1;
for(i=1;i<=N;i++)f[i]=i;
gauss(a,N,*prime);
for(i=1;i<=*prime;i++)
{
x=0;
for(j=1;j<=N;j++)if(a[i][j])x=j;
if(x==0)continue;
for(j=1;j<=N;j++)if(a[i][j])set_merge(j,x);
}
for(i=1;i<=*prime;i++)
{
x=0;
for(j=1;j<=N;j++)if(a[i][j])x=j;
if(x==0)continue;
cnt_e[set_find(x)]++;
}
for(i=1;i<=N;i++)cnt_x[set_find(i)]++;
for(i=1;i<=N;i++)
if(set_find(i)==i)ans<<=cnt_x[i]-cnt_e[i];
printf("%lld\n",ans-1);
}
int main()
{
ll T=read();
shai();
while(T--)
{
init();
work();
}
return 0;
}