bzoj3168 [Heoi2013]钙铁锌硒维生素(矩阵求逆+匈牙利)

如果把ai替换成bj,这n个向量还是线性无关即可以。
我们求一个 CA=B,=>C=BA1 C ∗ A = B , => C = B ∗ A − 1
矩阵求逆可以高斯消元来做,注意他原本的A矩阵也不一定就线性无关qaq
如果 Cij!=0 C i j ! = 0 ,说明bj可以替换ai。因此 CT C T 就是这张二分图的邻接矩阵。

然后我们就要求一个字典序最小的完备匹配了。
可以先随便找一个完备匹配,然后从小到大的贪心,看能否在不影响前面已确定的点的情况下找到一条交替路使得自己的标号更小。
复杂度 O(n3) O ( n 3 )

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 310
#define mod 1000000007
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,bf[N];
bool mp[N][N],f[N];
inline void inc(int &x,int y){x+=y;x%=mod;}
inline void dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline int ksm(int x,int k){
    int res=1;for(;k;k>>=1,x=(ll)x*x%mod) if(k&1) res=(ll)res*x%mod;return res;
}
struct Matrix{
    int a[N][N];
    int* operator[](int x){return a[x];}
    void init(bool t){
        memset(a,0,sizeof(a));
        if(t) for(int i=1;i<=n;++i) a[i][i]=1;
    }friend Matrix operator*(Matrix a,Matrix b){
        Matrix res;res.init(0);
        for(int i=1;i<=n;++i)
            for(int k=1;k<=n;++k)
                for(int j=1;j<=n;++j)
                    inc(res[i][j],(ll)a[i][k]*b[k][j]%mod);return res;
    }Matrix inv(){
        Matrix c;c.init(1);
        for(int i=1;i<=n;++i){
            int r=i;while(r<=n&&!a[r][i]) ++r;
            if(r!=i) for(int j=1;j<=n;++j) swap(a[r][j],a[i][j]),swap(c[r][j],c[i][j]);
            int t=ksm(a[i][i],mod-2);
            for(int j=1;j<=n;++j) a[i][j]=(ll)a[i][j]*t%mod,c[i][j]=(ll)c[i][j]*t%mod;
            for(int j=1;j<=n;++j){
                if(j==i) continue;t=a[j][i];
                for(int k=1;k<=n;++k) dec(a[j][k],(ll)a[i][k]*t%mod),dec(c[j][k],(ll)c[i][k]*t%mod);
            }
        }return c;
    }
}a,b;
inline bool find(int x){
    for(int y=1;y<=n;++y){
        if(!mp[x][y]||f[y]) continue;f[y]=1;
        if(!bf[y]||find(bf[y])){bf[y]=x;return 1;}
    }return 0;
}
inline bool find2(int x,int id){
    for(int y=1;y<=n;++y){
        if(!mp[x][y]||f[y]) continue;f[y]=1;
        if(bf[y]==id||bf[y]>id&&find2(bf[y],id)){bf[y]=x;return 1;}
    }return 0;
}
int main(){
//  freopen("ferrous10.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j) a[i][j]=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j) b[i][j]=read();
    b=b*a.inv();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j) if(b[i][j]) mp[j][i]=1;
    for(int i=1;i<=n;++i){
        memset(f,0,sizeof(f));if(find(i)) continue;
        puts("NIE");return 0;
    }puts("TAK");
    for(int i=1;i<=n;++i){
        memset(f,0,sizeof(f));find2(i,i);
    }for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(bf[j]==i) printf("%d\n",j);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值