do_while_true

一言(ヒトコト)

Tutte 矩阵 求解一般图最大匹配

证明之类的还是直接看 2017 年 IOI 国家集训队论文 《基于线性代数的一般图匹配》(杨家齐)。

一般图完美匹配

定义(Tutte 矩阵):对于一个无向图 \(G=(V,E)\),定义 \(G\) 的 Tutte 矩阵为一个 \(n\times n\) 的矩阵 \(\tilde{A}(G)\),其中

\[\tilde{A} (G)=\left\{\begin{matrix} x_{i,j} & v_iv_j\in E 且 i<j \\ -x_{j,i} & v_iv_j\in E 且 i>j \\ 0 & otherwise \end{matrix}\right. \]

其中 \(x_{i,j}\) 是两两不同,仅与边 \(v_iv_j\) 有关的变量。

定理一:图 \(G\) 有完美匹配当且仅当 \(\det\tilde{A}(G)\neq 0\)

这个姑且还是学了一下怎么证。

一个图的环覆盖是说,用若干个环去覆盖 \(G\) 的所有节点,使得 \(G\) 的每个节点都恰好在一个环中。如果环长均为偶数,那么称其为一个偶环覆盖。

\(G\) 的每一个环覆盖都能和一个长度为 \(|V|\) 的置换所对应,具体地,对于一个排列 \(\pi\),其对应的环覆盖为选出所有 \(v_iv_{\pi(i)}\) 的边。

引理一:图 \(G=(V,E)\) 有完美匹配当且仅当 \(G\) 存在偶环覆盖。

\(G\) 存在完美匹配,将所有匹配边看作一个二元环,即为一个偶环覆盖。

\(G\) 存在一个偶环覆盖,那么在每个偶环上间隔一条边取一条边,即可得到一个完美匹配。

故引理成立。

想要证得定理一,我们要说明 \(\det\tilde{A}(G)\neq 0\) 当且仅当 \(G\) 存在偶环覆盖。

根据行列式的定义有:

\[\det\tilde{A}(G)=\sum_{\pi}(-1)^{\mathrm{sgn}(\pi)}\prod \tilde{A}(G)_{k,\pi(k)} \]

考虑任意一个环覆盖对应的置换,将其中一个环反向,由于环的个数也就是置换的奇偶性没有改变,也就是其逆序对个数,\(\mathrm{sgn}(\pi)\) 没有改变。

考虑这个环对乘积的贡献,根据 \(\tilde{A}(G)\) 的定义可知,将一个环反向后,环上每一条边都贡献了一个 \(-1\),所以对一个奇环反向,其对应置换的权值会 \(\times (-1)\),否则并不会变。

这告诉我们,对于每一个存在奇环的环覆盖,将其其中一个奇环反向,这两个环覆盖的权值恰好相反,则所有存在奇环的环覆盖都会互相抵消掉,不会对 \(\det\tilde{A}(G)\) 产生贡献。

由于 \(\tilde{A}(G)\)\(x_{i,j}\) 互不相同,所以若存在一个偶环覆盖,它一定不会被其他的偶环覆盖所消掉,则 \(\det\tilde{A}(G)\neq 0\)

综上所述 \(\det\tilde{A}(G)\neq 0\) 当且仅当 \(G\) 存在一个偶环覆盖,即 \(G\) 存在完美匹配。

现在问题变成了算 \(\det\tilde{A}(G)\),如果暴力做多项式操作复杂度将变得很大,由于最终不需要知道其取值为多少,仅需要判断最终多项式取值是否为 \(0\)

引理二 (Schwartz, Zippel):对于域 \(\mathbb{F}\) 上的一个不恒为 \(0\)\(n\)\(d\) 度多项式 \(P(x_1, x_2,\cdots, x_n)\)
\(r_1,r_2,\cdots,r_n\)\(n\)\(\mathbb{F}\) 中的独立选取的随机数,则:

\[Pr[P(r_1,r_2,\cdots,r_n) = 0]\leq \frac{d}{|\mathbb{F}|} \]

证明还是看原论文。

人话:检查一个 \(n\) 次多项式是否恒为 \(0\),选取模 \(p\) 意义下的若干随机数代入到各个元中,通过最终结果判断多项式是否恒为 \(0\),概率是 \(\frac{n}{p}\)

所以选取较大的 \(p\),或者多次随机,即可完成 \(\det\tilde{A}(G)\neq 0\) 的判断。

完美匹配方案构造

太难了,先咕着。

一般最大匹配

定理:图 \(G\) 的最大匹配的大小 \(ν(G)\)\(\frac{1}{2}\mathrm{rank}\tilde{A}(G)\)

求这个矩阵的秩也可以用在模 \(p\) 意义下给每个元随机权值的方式。

证明咕着。

贴个代码:

const int N=1010;
const ll mod=998244353;
inline void cdel(ll &x,ll y){x=(x-y<0)?(x-y+mod):(x-y);}
inline ll del(ll x,ll y){return (x-y<0)?(x-y+mod):(x-y);}
ll qpow(ll x,ll y){
	ll s=1;
	while(y){
		if(y&1)s=s*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return s;
}
int n,m;
ll v[N][N];
ll w[N*N];
pii e[N*N];
mt19937 rnd(0);
map<int,int>vis;
int Solve(){
	int c=0;
	for(int i=1;i<=n;i++){
		int p=0;
		for(int j=c+1;j<=n;j++)
			if(v[j][i]){
				p=j;
				break;
			}
		if(!p)continue;
		++c;
		swap(v[c],v[p]);
		ll rev=qpow(v[c][i],mod-2);
		for(int j=c+1;j<=n;j++){
			ll tmp=v[j][i]*rev%mod;
			for(int k=1;k<=n;k++)
				cdel(v[j][k],tmp*v[c][k]%mod);
		}
	}
	return c;
}
int main(){
	read(n,m);
	for(int i=1;i<=m;i++){
		int u,v;read(u,v);
		if(u>v)swap(u,v);
		e[i]=mp(u,v);
	}
	int ans=0;
	for(int o=1;o<=5;o++){
		for(int i=1;i<=m;i++)w[i]=rnd()%998244352+1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				v[i][j]=0;
		for(int i=1;i<=m;i++){
			v[e[i].fi][e[i].se]=w[i];
			v[e[i].se][e[i].fi]=0;
			cdel(v[e[i].se][e[i].fi],w[i]);
		}
		int now=Solve()/2;
		++vis[now];
		if(vis[now]>vis[ans])
			ans=now;
	}
	cout << ans << '\n';
	return 0;
}

最大匹配方案构造

posted @ 2022-07-27 16:14  do_while_true  阅读(146)  评论(0编辑  收藏  举报