题目:https://www.luogu.org/problemnew/show/P4336
当作考试题了,然而没想出来,呵呵。
其实不是二分图完美匹配方案数,而是矩阵树定理+容斥...
就是先放上所有的边,求生成树个数,但其中可能有的公司的边没有选上,所以减去至少一个公司没选上的,加上两个...
高斯消元里面可以直接除而不用辗转相除,因为取模可以乘逆元,反倒是辗转相除里不能直接用除法,会减不到0。
代码如下:
#include#include #include #include #include #define pb push_back using namespace std; typedef long long ll; int const xn=20,xm=400,mod=1e9+7; int n,m[xn],id[xn][xn],deg[xn][xn],sid[xn][xn],ans,cnt; ll a[xn][xn]; vector<int>vc[xn]; struct N{int u,v;}ed[xm]; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } ll pw(ll a,int b) { ll ret=1; for(;b;b>>=1,a=(a*a)%mod)if(b&1)ret=(ret*a)%mod; return ret; } int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;} int gauss() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)a[i][j]=upt(deg[i][j]-sid[i][j]); int fl=1; for(int i=1;i ) { int t=i; for(int j=i+1;j ) if(a[j][i]>a[t][i])t=j; if(t!=i) { fl=-fl; for(int j=1;j )swap(a[i][j],a[t][j]); } for(int j=i+1;j ) { int tmp=(ll)a[j][i]*pw(a[i][i],mod-2)%mod;//a[j][i]/a[i][i] for(int k=i;k ) a[j][k]=upt(a[j][k]-(ll)tmp*a[i][k]%mod); } } ll ret=1; for(int i=1;i mod; return ret*fl; } void dfs(int nw,int s) { if(nw==n) { int sum=gauss(); if((s&1)==((n-1)&1))ans+=sum; else ans-=sum; ans=upt(ans); return; } dfs(nw+1,s); int siz=vc[nw].size(); for(int i=0;i ) { int u=ed[vc[nw][i]].u,v=ed[vc[nw][i]].v; deg[u][u]++; deg[v][v]++; sid[u][v]++; sid[v][u]++; } dfs(nw+1,s+1); for(int i=0;i ) { int u=ed[vc[nw][i]].u,v=ed[vc[nw][i]].v; deg[u][u]--; deg[v][v]--; sid[u][v]--; sid[v][u]--; } } int main() { n=rd(); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) ed[++cnt].u=i,ed[cnt].v=j,id[i][j]=id[j][i]=cnt; for(int i=1;i ) { m[i]=rd(); for(int j=1,x,y;j<=m[i];j++)x=rd(),y=rd(),vc[i].pb(id[x][y]); } dfs(1,0); printf("%d\n",ans); return 0; }