tutte矩阵
一般图的最大匹配数为tutte矩阵的秩(必为偶数)除2
求出最大匹配后加入(n-2*最大匹配数)个点与所有点连边,新图一定存在最大匹配
与新点匹配的点在原图中不与任何点匹配
求一张图的完美匹配时可尝试删除一条边,两个点看是否仍存在完美匹配
有结论两点i,j可能在最大匹配中当且仅当
A的逆矩阵[j][i]不为0且i,j有边相连
删除一条边时可以以第i行的第j列为主元,第j行的第i列为主元消元维护逆矩阵
复杂度O(n^3)常数巨大。
#pragma GCC optimize(2) #include <cstdio> #include <iostream> #include <cmath> #define LL long long using namespace std; const LL mo=1e9+7; LL sed=12939512; int n,m; int b[2001],fin[2001]; int sid[124751][2],cnt,edge[2001][2001]; LL ran(){ sed*=21409124;sed+=1249134;sed^=1234904;sed%=mo; return(sed); } LL qpow(LL bas,int powe){ LL ret=1; for (;powe;bas*=bas,bas%=mo){ if (powe&1) ret*=bas,ret%=mo; powe>>=1; } return(ret); } struct matrix{ LL a[1201][1201],tmp[1201][1201]; int n,m; int getrank(){ for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) tmp[i][j]=a[i][j]; for (int i=1;i<=n;i++){ for (int j=i;j<=n;j++) if (a[j][i]) {swap(a[j],a[i]);break;} if (!a[i][i]) continue; LL inv=qpow(a[i][i],mo-2); for (int j=i+1;j<=n;j++){ LL bas=a[j][i]*inv%mo; if (!bas) continue; for (int k=i;k<=n;k++) if (a[i][k]) a[j][k]-=a[i][k]*bas%mo,a[j][k]%=mo,a[j][k]+=mo,a[j][k]%=mo; } } LL ret=0; for (int i=1;i<=n;i++) if (a[i][i]) ret++; return(ret); } void getinv(){ for (int i=1;i<=n;i++) a[i][n+i]=1; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j]%mo+mo)%mo; for (int i=1;i<=n;i++){ int po;LL maxi=0; for (int j=i;j<=n;j++){ if (abs(a[j][i])>maxi){ maxi=abs(a[j][i]);po=j; } } for (int j=1;j<=2*n;j++){ LL t=a[i][j];a[i][j]=a[po][j];a[po][j]=t; } if (abs(maxi)==0) continue; LL rev=qpow(a[i][i],mo-2); for (int j=i+1;j<=n;j++){ LL tim=a[j][i]*rev%mo; for (int k=i;k<=2*n;k++) a[j][k]-=a[i][k]*tim%mo,a[j][k]%=mo; } } for (int i=1;i<=n;i++) for (int j=1;j<=2*n;j++) a[i][j]=(a[i][j]%mo+mo)%mo; for (int i=n;i>=1;i--){ for (int j=i+1;j<=n;j++){ for (int k=n+1;k<=2*n;k++) a[i][k]-=a[i][j]*a[j][k]%mo,a[i][k]%=mo; a[i][j]=0; } for (int j=n+1;j<=2*n;j++) a[i][j]*=qpow(a[i][i],mo-2),a[i][j]%=mo; a[i][i]=1; } for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j+n]%mo+mo)%mo; } void elim(int x,int y){ for (int i=1;i<=n;i++) if (!b[i]){ LL bas=a[i][y]*qpow(a[x][y],mo-2)%mo; for (int j=1;j<=n;j++){ a[i][j]-=a[x][j]*bas%mo; a[i][j]%=mo;a[i][j]+=mo;a[i][j]%=mo; } } } }a; int main(){ scanf("%d%d",&n,&m); a.n=a.m=n; for (int i=1;i<=m;i++){ int t1,t2; scanf("%d%d",&t1,&t2); sid[++cnt][0]=t1;sid[cnt][1]=t2; LL num=ran(); a.a[min(t1,t2)][max(t1,t2)]=num; a.a[max(t1,t2)][min(t1,t2)]=mo-num; } int ans=a.getrank()/2; printf("%d\n",ans); int addin=n-ans*2; a.n=n+addin;a.m=n+addin; for (int i=1;i<=a.n;i++) for (int j=1;j<=a.m;j++) a.a[i][j]=0; for (int i=1;i<=cnt;i++){ LL num=ran(); a.a[min(sid[i][0],sid[i][1])][max(sid[i][0],sid[i][1])]=num; a.a[max(sid[i][0],sid[i][1])][min(sid[i][0],sid[i][1])]=-num; edge[min(sid[i][0],sid[i][1])][max(sid[i][0],sid[i][1])]=1; edge[max(sid[i][0],sid[i][1])][min(sid[i][0],sid[i][1])]=1; } for (int i=1;i<=addin;i++) for (int j=1;j<=n;j++){ LL num=ran(); a.a[j][n+i]=num; a.a[n+i][j]=-num; edge[j][n+i]=edge[n+i][j]=1; } a.getinv(); for (int i=1;i<=n;i++) if (!b[i]){ for (int j=i+1;j<=n+addin;j++) if (edge[i][j]&&a.a[j][i]!=0&&!b[j]){ fin[i]=j;fin[j]=i; b[i]=1;b[j]=1; a.elim(i,j); a.elim(j,i); break; } } for (int i=1;i<=n;i++) printf("%d ",(fin[i]<=n ? fin[i]:0)); }
-------------------------------
以上方法可以解决必选点等问题。若只需求最大匹配,可发现最大匹配的点为tutte矩阵中最大线性无关组所对应的点。求最大匹配即可。
在求逆和消元中进行一定的常数优化。
#include <cstdio> #include <iostream> #include <cmath> #define LL long long using namespace std; const LL mo=1e9+7; LL sed=12939512; struct linknode{ int pre,next; }row[2001],col[2001]; int n,m; int b[2001],fin[2001]; int sid[124751][2],cnt,edge[2001][2001],lis[2001],inlis[2001]; LL ran(){ sed*=21409124;sed+=1249134;sed^=1234904;sed%=mo; return(sed); } LL qpow(LL bas,int powe){ LL ret=1; for (;powe;bas*=bas,bas%=mo){ if (powe&1) ret*=bas,ret%=mo; powe>>=1; } return(ret); } struct matrix{ LL a[1201][1201],tmp[1201][1201]; int n,m; int getrank(){ for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) tmp[i][j]=a[i][j]; for (int i=1;i<=n;i++){ for (int j=i;j<=n;j++) if (a[j][i]) {swap(a[j],a[i]);break;} if (!a[i][i]) continue; LL inv=qpow(a[i][i],mo-2); for (int j=i+1;j<=n;j++){ LL bas=a[j][i]*inv%mo; if (!bas) continue; for (int k=i;k<=n;k++) if (a[i][k]) a[j][k]-=a[i][k]*bas%mo,a[j][k]%=mo,a[j][k]+=mo,a[j][k]%=mo; } } LL ret=0; for (int i=1;i<=n;i++) if (a[i][i]) ret++; return(ret); } void getinv(){ for (int i=1;i<=n;i++) a[i][n+i]=1; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j]%mo+mo)%mo; for (int i=1;i<=n;i++){ int po;LL maxi=0; for (int j=i;j<=n;j++){ if (abs(a[j][i])>maxi){ maxi=abs(a[j][i]);po=j; } } for (int j=1;j<=2*n;j++){ LL t=a[i][j];a[i][j]=a[po][j];a[po][j]=t; } if (abs(maxi)==0) continue; LL rev=qpow(a[i][i],mo-2); for (int j=i+1;j<=n;j++){ if (!a[j][i]) continue; LL tim=a[j][i]*rev%mo; for (int k=i;k<=2*n;k++) if (a[i][k]) a[j][k]-=a[i][k]*tim%mo,a[j][k]%=mo; } } for (int i=1;i<=n;i++) for (int j=1;j<=2*n;j++) a[i][j]=(a[i][j]%mo+mo)%mo; for (int i=n;i>=1;i--){ for (int j=i+1;j<=n;j++) if (a[i][j]){ for (int k=n+1;k<=2*n;k++) if (a[j][k]) a[i][k]-=a[i][j]*a[j][k]%mo,a[i][k]%=mo; a[i][j]=0; } LL rev=qpow(a[i][i],mo-2); for (int j=n+1;j<=2*n;j++) if (a[i][j]) a[i][j]*=rev,a[i][j]%=mo; a[i][i]=1; } for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j+n]%mo+mo)%mo; } void elim(int x,int y){ for (int i=0;i<=n;i=row[i].next) if (!b[i]){ LL bas=a[i][y]*qpow(a[x][y],mo-2)%mo; for (int j=col[0].next;j<=n;j=col[j].next){ a[i][j]-=a[x][j]*bas%mo; a[i][j]%=mo;a[i][j]+=mo;a[i][j]%=mo; } } } }a; void del(int po){ b[po]=1; row[row[po].pre].next=row[po].next; row[row[po].next].pre=row[po].pre; col[col[po].pre].next=col[po].next; col[col[po].next].pre=col[po].pre; } int main(){ scanf("%d%d",&n,&m); a.n=a.m=n; for (int i=1;i<=m;i++){ int t1,t2; scanf("%d%d",&t1,&t2); sid[++cnt][0]=t1;sid[cnt][1]=t2; LL num=ran(); a.a[min(t1,t2)][max(t1,t2)]=num; a.a[max(t1,t2)][min(t1,t2)]=mo-num; } int ans=a.getrank()/2; printf("%d\n",ans); m=0; for (int i=1;i<=n;i++) if (a.a[i][i]) lis[++m]=i,inlis[i]=m; for (int i=1;i<=m;i++){ row[i].pre=i-1;row[i].next=i+1; col[i].pre=i-1;col[i].next=i+1; } row[0].next=col[0].next=1; for (int i=1;i<=a.n;i++) for (int j=1;j<=a.m;j++) a.a[i][j]=0; a.n=m;a.m=m; for (int i=1;i<=cnt;i++) if (inlis[sid[i][0]]&&inlis[sid[i][1]]){ LL num=ran(),u=inlis[sid[i][0]],v=inlis[sid[i][1]]; if (u>v) swap(u,v); a.a[u][v]=num; a.a[v][u]=-num; edge[u][v]=1;edge[v][u]=1; } a.getinv(); for (int i=1;i<=m;i++) if (!b[i]){ for (int j=i+1;j<=m;j++) if (edge[i][j]&&a.a[j][i]!=0&&!b[j]){ fin[lis[i]]=lis[j];fin[lis[j]]=lis[i]; del(i);del(j); a.elim(i,j); a.elim(j,i); break; } } for (int i=1;i<=n;i++) printf("%d ",fin[i]); }