Tutte 矩阵 求解一般图最大匹配
证明之类的还是直接看 2017 年 IOI 国家集训队论文 《基于线性代数的一般图匹配》(杨家齐)。
一般图完美匹配
定义(Tutte 矩阵):对于一个无向图 \(G=(V,E)\),定义 \(G\) 的 Tutte 矩阵为一个 \(n\times n\) 的矩阵 \(\tilde{A}(G)\),其中
其中 \(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\) 存在偶环覆盖。
根据行列式的定义有:
考虑任意一个环覆盖对应的置换,将其中一个环反向,由于环的个数也就是置换的奇偶性没有改变,也就是其逆序对个数,\(\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}\) 中的独立选取的随机数,则:
证明还是看原论文。
人话:检查一个 \(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;
}
最大匹配方案构造
咕