UVA11542 - Square

链接

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 cntecntx
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 &lt; c n t x cnt_e&lt;cnt_x cnte<cntx,说明这些变量的取值不定,但是只要给出了其中 c n t x − c n t e cnt_x-cnt_e cntxcnte个变量的取值,另一些的值就唯一确定了
关于如何判断一个变量是不是无解,在 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 (2cntxcnte)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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值