zoukankan      html  css  js  c++  java
  • [矩阵求逆+二分图匹配]BZOJ 3168 [Heoi2013]钙铁锌硒维生素

    题目梗概

    给出两个(n*n)的矩阵(A,B),将(A,B)中的向量进行匹配,使得(A)的任意向量被匹配的向量替换后,(A)仍线性无关,求字典序最小解,保证初始时(A)线性无关.

    解题思路

    因为(A)(n)维空间中的极大线性无关,所以(A)(n)维空间的一组基,(B)中的任意向量可以用(A)的组合表示.

    然后有一个结论就是如果(B)中的向量(i)能替换(A)中的向量(j),那么用(A)表示(i)的时候(j)的系数不为(0).

    感觉比较显然...于是设系数矩阵(C),有(C*A=B),(C=B*A^{-1}).

    关于矩阵求逆:在把(A)消成单位矩阵的时候,对一个单位矩阵进行同样的初等变换.

    然后(C)的转置显然就是邻接矩阵.

    一些细节:字典序最小的求法就是先刷一遍匈牙利然后贪心的修正一遍,因为我们只关心(C)中元素是否为零,所以用任意的一个质数在矩阵求逆的时候进行取模就行.

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    using namespace std;
    const int maxn=305,tt=1e9+7,maxm=90005;
    int n,f[maxn],ans[maxn];
    bool vis[maxn];
    int qsm(int w,int b){int num=1;while(b){if (b&1) num=(LL)num*w%tt;w=(LL)w*w%tt;b>>=1;}return num;}
    int a[maxn][maxn],b[maxn][maxn],c[maxn][maxn];
    void mul(int c[][maxn],int a[][maxn],int b[][maxn]){
    	for (int i=1;i<=n;i++)
    	for (int j=1;j<=n;j++){
    		c[i][j]=0;for (int k=1;k<=n;k++) (c[i][j]+=(LL)a[i][k]*b[k][j]%tt)%=tt;
    	}
    }
    void get_INV(){
    	for (int i=1;i<=n;i++){
    		if (!a[i][i]) for (int j=i+1;j<=n;j++) if (a[j][i]) swap(a[i],a[j]),swap(c[i],c[j]);
    		int w=qsm(a[i][i],tt-2);for (int j=1;j<=n;j++) a[i][j]=(LL)a[i][j]*w%tt,c[i][j]=(LL)c[i][j]*w%tt;
    		for (int j=1;j<=n;j++) if (a[j][i]&&i!=j){
    			w=a[j][i];for (int k=1;k<=n;k++) a[j][k]=(a[j][k]-(LL)a[i][k]*w%tt+tt)%tt,c[j][k]=(c[j][k]-(LL)c[i][k]*w%tt+tt)%tt;
    		}
    	}
    }
    bool find(int x){
    	for (int i=1;i<=n;i++) if (a[i][x]&&!vis[i]){
    		vis[i]=1;
    		if (!f[i]||find(f[i])){
    			f[i]=x;return 1;
    		}
    	} 
    	return 0;
    }
    bool work(int x,int fa){
    	for (int i=1;i<=n;i++) if (a[i][x]&&!vis[i]){
    		vis[i]=1;
    		if (f[i]==fa||(f[i]>fa&&work(f[i],fa))){
    			f[i]=x;return 1;
    		}
    	}
    	return 0;
    }
    int main(){
    	freopen("exam.in","r",stdin);
    	freopen("exam.out","w",stdout);
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&a[i][j]);
    	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&b[i][j]);
    	for (int i=1;i<=n;i++) c[i][i]=1;get_INV();mul(a,b,c);
    	for (int i=1;i<=n;i++){
    		memset(vis,0,sizeof(vis));
    		if (!find(i)){printf("NIE
    ");return 0;}
    	}
    	for (int i=1;i<=n;i++){
    		memset(vis,0,sizeof(vis));
    		work(i,i);
    	}
    	for (int i=1;i<=n;i++) ans[f[i]]=i;
    	printf("TAK
    ");for (int i=1;i<=n;i++) printf("%d
    ",ans[i]);
    	return 0;
    } 
    
  • 相关阅读:
    excel的宏与VBA实践——建表语句
    excel的宏与VBA入门(三)——流程控制
    excel的宏与VBA入门(二)——数据类型与变量
    excel的宏与VBA入门(一)——基础概念
    Python3入门(十二)——进程与线程
    kettle学习笔记(十)——数据检验、统计、分区与JS脚本
    kettle学习笔记(九)——子转换、集群与变量
    ISO/IEC 9899:2011 条款6.4.9——注释
    ISO/IEC 9899:2011 条款6.4.8——预处理数字
    ISO/IEC 9899:2011 条款6.4.7——头文件名
  • 原文地址:https://www.cnblogs.com/CHNJZ/p/10443662.html
Copyright © 2011-2022 走看看