高斯消元

18 篇文章 0 订阅
15 篇文章 0 订阅

题:
bzoj4184
bzoj3168
bzoj4031
bzoj1923
bzoj2115
bzoj1770
bzoj1013
bzoj3270
bzoj2419
bzoj2844
bzoj4269
bzoj3811
bzoj4820
bzoj3213
bzoj3759
bzoj3105
bzoj2322
bzoj3569
bzoj2155
bzoj3601*
bzoj3143

     //取模可求逆的数
     //求行列式的值
     void guass(){
    for (int i=1;i<=m;i++){
        if (a[i][i]==0){
            for (int j=i+1;j<=m;j++)
            if (a[j][i]){
                ans=-ans;
                for (int k=i;k<=m;k++)
                    swap(a[i][k],a[j][k]);
                break;
            }
        }
        if (a[i][i]==0){
            ans=0;
            return;
        }
        ans=(ans*a[i][i])%mod;
        for (int j=i+1;j<=m;j++)
        if (a[j][i]){
            ll tmp=a[j][i]*power(a[i][i],mod-2)%mod;
            for (int k=i;k<=m;k++)  
                a[j][k]=(a[j][k]-tmp*a[i][k]%mod+mod)%mod;
        }
    }
    ans=(ans+mod)%mod;
}

bzoj4184

题意:

给出一堆数的插入和删除顺序,询问每一次操作后,选出某些数异或起来的最大值 a1 ^ a2 ^ a3 ^…^ an
(数的个数 500000 )

分析:

有插入和删除操作不好处理,于是我们尝试经过一波预处理将其转化为只有插入~
显然一个数的出现时间是一段一段的,离散化后记录每个数当前的个数便可以知道在哪段区间。
由于我们要求出每一个时间点的答案,便用线段树结构记录某个数覆盖的区间,在最后的dfs时不断插入到线性基中,到线段树的叶节点时求出答案。

//核心代码
int getans(base nw){//查询最大值
    int res=0;
    for (int i=30;i>=0;i--)
        if (nw.v[i])
            res^=nw.v[i];
    return res;
}

void ins(base &nw,int x){//向线性基中插入x
    int ps=-1;
    for (int i=30;i>=0;i--)//从最高位开始贪心插入
    if ((x>>i)&1){
        if (!nw.v[i] && ps==-1)
            ps=i; else
            x^=nw.v[i];
    }
    if (~ps){
        nw.v[ps]=x;
        for (int i=30;i>ps;i--)
        if ((nw.v[i]>>ps)&1)
            nw.v[i]^=x;
    }
}
void dfs(int rt,int l,int r,base nw){
    for (int i=h[rt];i;i=g[i].next)
        ins(nw,g[i].y);
    if (l==r){
        ans[l]=getans(nw);
        return;
    }
    int mid=(l+r)>>1;
    dfs(2*rt,l,mid,nw);
    dfs(2*rt+1,mid+1,r,nw);
}

bzoj3168

题意:

给出一个矩阵A和B,满足两矩阵大小相同。求一个字典序最小的 1...n 的排列 a 满足将随意一个—Ai换成 Bai 后矩阵A仍然满秩。( n300

分析:

容易想到二分图,但问题在于如何在O( n3 )内完成构图。
考虑一个行向量 Bi ,我们在A中找到最小的行向量集合满足Bi能够被这些行向量线性表出,那么显然Bi仅仅能替换这些行向量

我们能够设矩阵 C 满足CA=B,那么 C=BA1
Ci,j0 表示 Bi 的线性表出须要 Aj ,因此 CT 就是这个二分图的邻接矩阵

如今我们有了一个二分图,怎样求字典序最小的完备匹配呢?

我们能够枚举每一条边,然后推断剩余的图是否存在一个完备匹配。可是这样做是 O(n4)

我们能够跑两遍匈牙利算法,第一遍求出随意一个完备匹配。第二遍对于每一个点贪心选最小的出边推断是否能找到不影响前面点的交错环

总时间复杂度 O(n3)

Matrix Get_Inv(Matrix a)//矩阵求逆
    {
        Matrix re(true);//单位矩阵
        int i,j,k;
        for(i=1;i<=n;i++)
        {
            for(k=i;k<=n;k++)
                if(a[k][i])
                    break;
            for(j=1;j<=n;j++)
            {
                swap(a[i][j],a[k][j]);
                swap(re[i][j],re[k][j]);
            }
            long long inv=Quick_Power(a[i][i],MOD-2);
            for(j=1;j<=n;j++)
            {
                a[i][j]=a[i][j]*inv%MOD;
                re[i][j]=re[i][j]*inv%MOD;
            }
            for(k=1;k<=n;k++)
                if(k!=i)
                {
                    long long temp=(MOD-a[k][i])%MOD;
                    for(j=1;j<=n;j++)
                    {
                        (a[k][j]+=a[i][j]*temp%MOD)%=MOD;
                        (re[k][j]+=re[i][j]*temp%MOD)%=MOD;
                    }
                }
        }
        return re;
    }

bzoj4031

题意:

给出一个”.”“#”矩阵,”.”与”.”之间能连边,使得任意两点之间有且只有一条路径相连。求方案总数。 (n,m9)

分析:

显然是矩阵树定理。给矩阵每一个点标号,若两相邻点x,y(编号),x能**向**y连边就将行列式 a[x][x]++,a[x][y]
最后求一波行列式的值即可。(将行列式左下角的值全用高斯消元消成0,对角线乘积就是答案)
注意:取模高斯消元的奇怪姿势

long long slove(){
    for (int i=1;i<tot;i++)
        for (int j=1;j<tot;j++)
            a[i][j]=(a[i][j]+mod)%mod;
    long long ans=1,f=1;
    for (int i=1;i<tot;i++){
        for (int j=i+1;j<tot;j++){
            long long A=a[i][i],B=a[j][i];
            while (B!=0){//用欧几里德gcd求法理解
                long long t=A/B;A%=B;
                swap(A,B);
                for (int k=i;k<tot;k++)
                    a[i][k]=(a[i][k]-t*a[j][k]%mod+mod)%mod;
                for (int k=i;k<tot;k++)
                    swap(a[i][k],a[j][k]);
                f=-f;
            }
        }
        if (!a[i][i]) return 0;
        ans=ans*a[i][i]%mod;
    }
    return (ans*f+mod)%mod;
}

bzoj3270

题意:

给出一个无向图,两个人分别从 s,t 出发,在第i个点有 p[i] 的几率停留,不停留则等几率地跑到相邻的点上,询问两人同时到同一点的几率是多少。输出所以点的几率。(点数 20

分析:

从最终状态入手,也就是说当两个人同时到达 (x1,x2) ,显然这个状态是从其他状态转换过来的,也就是相邻的点。
其状态可能为 (x1,x2)(x1,pre2)(pre1,x2)(pre1,pre2)
这4种状态转换到 (x1,x2) 的概率分别为
p[x1]p[x2]p[x1](1p[x2](1p[x1])p[x2](1p[x1])(1p[x2])
于是每一个点都有一条表示它的答案的表达式。也就是说我们有n个点n条表达式(即方程),再用高斯消元乱搞就好了。

bzoj3143

题意:

一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

分析:

给m条边编号,显然期望经过次数越多的边编号越小。
g[i] 为第i条经过的期望次数。
经过第i条边必然是从第i条边的一端(X)到另一端(Y)。
也就是说我们要知道经过每个点的期望次数。

f[i] 为第i个经过的期望次数。
显然: f[i]+=f[i>next]deg[i>next] ( deg[i] 为第i个点的出度)
于是我们有n个未知数 f[] 与n条不等式 f[i]=......
那么高斯消元乱搞就好啦~~~

嗯,求出 f[i] 以后。现在要求的就是 g[] 了。
显然: g[i]=f[x]deg[x]+f[y]deg[y] (x,y为第i条边的两端)

void guass(){//高斯消元模板
    for (int i=1;i<=n;i++){
        int id=i;ld mx=-1.0;
        for (int j=i;j<=n;j++)
            if (fabs(a[j][i])+eps>mx)
                mx=fabs(a[j][i])+eps,id=j;
        if (id>n) continue;
        if (id!=i) swap(a[i],a[id]);
        for (int j=i+1;j<=n;j++)
            if (fabs(a[j][i])>eps){
                ld t=a[j][i]/a[i][i];
                for (int k=i;k<=n+1;k++)
                    a[j][k]-=t*a[i][k];
            }
    }
    for (int i=n;i;i--){
        for (int j=i+1;j<=n;j++)
            a[i][n+1]-=a[i][j]*a[j][n+1];
        a[i][n+1]/=a[i][i];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值