• BZOJ 3168: [Heoi2013]钙铁锌硒维生素 [线性基 Hungary 矩阵求逆]


    3168: [Heoi2013]钙铁锌硒维生素

    题意:给一个线性无关组A,再给一个B,要为A中每个向量在B中选一个可以代替的向量,替换后仍然线性无关。判断可行和求字典序最小的解


    PoPoQQQ orz

    显然是一个二分图匹配的模型

    A是一个线性基,用它把B中每个向量表示出来,那么(B_i)可以替换(A_j)当且仅当表示(B_i)用到了(A_j)

    可是A并不是每一位独立,怎么求表示啊?

    A和B可以看成两个矩阵(横向量组)
    (C*A=B ightarrow C=B*A^{-1})
    (C_{i,j}=1)说明表示(B_i)用到了(A_j),那么(C^T)就是这个二分图的邻接矩阵了

    求矩阵的逆

    这里说一种方法,对A进行高斯约当消元,右面的常数列换成单位矩阵。校园后,左面变成了单位矩阵,右面就是(A^{-1})

    二分图匹配字典序最小的解

    求任意一个完美匹配,然后从1到n贪心选择字典序最小的解,方法和hungary类似,但是要比较匹配点和当前点的字典序

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    using namespace std;
    typedef unsigned long long ll;
    const int N=305, P=1e9+7;
    inline int read() {
    	char c=getchar(); int x=0, f=1;
    	while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    	while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
    	return x*f;
    }
    
    inline ll Pow(ll a, int b) {
    	ll ans=1;
    	for(; b; b>>=1, a=a*a%P)
    		if(b&1) ans=ans*a%P;
    	return ans;
    }
    inline void mod(int &x) {if(x<0) x+=P; else if(x>=P) x-=P;}
    
    int n, g[N][N]; char s[N];
    struct Matrix {
    	int a[N][N];
    	Matrix(){memset(a, 0, sizeof(a));}
    	int* operator [](int x) {return a[x];}
    	inline void im() {for(int i=1; i<=n; i++) a[i][i]=1;}
    	void print() {for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) printf("%d%c",a[i][j],j==n?'
    ':' ');}
    }a, b, c;
    
    Matrix inverse(Matrix a) {
    	Matrix c; c.im();
    	for(int i=1; i<=n; i++) {
    		int r;
    		for(r=i; r<=n; r++) if(a[r][i]) break;
    		// r != n+1
    		if(r!=i) for(int j=1; j<=n; j++) 
    			swap(a[i][j], a[r][j]), swap(c[i][j], c[r][j]);
    		ll inv = Pow(a[i][i], P-2); 
    		for(int j=1; j<=n; j++) 
    			a[i][j] = a[i][j]*inv%P, c[i][j] = c[i][j]*inv%P;
    		for(int k=1; k<=n; k++) if(k!=i) {
    			ll t = a[k][i]%P;
    			for(int j=1; j<=n; j++) 
    				mod(a[k][j] -= a[i][j]*t%P), mod(c[k][j] -= c[i][j]*t%P);
    		}
    	}
    	return c;
    }
    Matrix operator *(Matrix a, Matrix b) {
    	Matrix c;
    	for(int i=1; i<=n; i++)
    		for(int k=1; k<=n; k++) if(a[i][k])
    			for(int j=1; j<=n; j++) 
    				mod(c[i][j] += (ll)a[i][k]*b[k][j]%P);
    	return c;
    }
    
    int vis[N], le[N];
    bool dfs(int u) {
    	for(int v=1; v<=n; v++) 
    		if(!vis[v] && g[u][v]) {
    			vis[v]=1;
    			if(!le[v] || dfs(le[v])) {
    				le[v]=u;
    				return true;
    			}
    		}
    	return false;
    }
    bool dfs(int u, int now) {
    	for(int v=1; v<=n; v++) 
    		if(!vis[v] && g[u][v]) {
    			vis[v]=1;
    			if(le[v]==now || (le[v]>now && dfs(le[v], now))) {
    				le[v]=u;
    				return true;
    			}
    		}
    	return false;
    }
    int main() {
    	freopen("in","r",stdin);
    	//freopen("ferrous.in","r",stdin);
    	//freopen("ferrous.out","w",stdout);
    	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();
    	c = b * inverse(a); //puts("c");c.print();
    	//Matrix t = a * inverse(a); puts("t"); t.print();
    	for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(c[i][j]) g[j][i]=1;
    	for(int i=1; i<=n; i++) {
    		memset(vis, 0, sizeof(vis));
    		if(!dfs(i)) {puts("NIE"); return 0;}
    	}
    	puts("TAK");
    	for(int i=1; i<=n; i++) {
    		memset(vis, 0, sizeof(vis));
    		dfs(i, i);
    	}
    	for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(le[j]==i) printf("%d
    ",j);
    }
    
  • 相关阅读:
    2-SAT 问题及图论解法
    割点、强连通分量
    欧拉路、欧拉回路
    DFS 树
    【题解】[AGC 034 F] RNG and XOR【异或卷积 FWT】
    【题解】[UOJ 62] UR #5 怎样跑得更快【莫比乌斯反演】
    【题解】LOJ #2085 / 洛谷 P1587「NOI2016」循环之美【莫比乌斯反演】
    【题解】LOJ #6052 「雅礼集训 2017 Day11」DIV【莫比乌斯反演】
    【题解】[HDU 5382] GCD?LCM【莫比乌斯反演 差分 线性筛】
    【题解】[51Nod 1847] 奇怪的数学题【min_25筛 杜教筛 莫比乌斯反演】
  • 原文地址:https://www.cnblogs.com/candy99/p/6659105.html
Copyright © 2020-2023  润新知